├── .babelrc
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── README.md
├── compile
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
├── App.tsx
├── api
│ ├── api.ts
│ └── service.ts
├── assets
│ ├── img
│ │ └── time.png
│ └── style
│ │ └── index.scss
├── components
│ └── CommonTitle
│ │ └── index.vue
├── index.html
├── index.ts
├── lib
│ ├── axios.ts
│ └── utils.ts
├── mock
│ └── index.ts
├── route.ts
├── routes
│ ├── index
│ │ ├── index.scss
│ │ └── index.tsx
│ └── test
│ │ ├── index.scss
│ │ └── index.tsx
├── store
│ ├── index.ts
│ └── modules
│ │ └── global.ts
└── types
│ ├── images.d.ts
│ ├── index.d.ts
│ └── vue.d.ts
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": [
4 | "@babel/plugin-transform-runtime",
5 | "transform-vue-jsx",
6 | ["@babel/plugin-proposal-decorators", { "legacy": true }],
7 | "@babel/plugin-proposal-class-properties",
8 | ]
9 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/**/*
2 | compile/*
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | module.exports = {
3 | parser: "vue-eslint-parser",
4 | parserOptions: {
5 | parser: "@typescript-eslint/parser",
6 | sourceType: "module"
7 | },
8 | extends: [
9 | "airbnb-base",
10 | "plugin:@typescript-eslint/recommended"
11 | ],
12 | plugins: [
13 | "vue",
14 | "@typescript-eslint"
15 | ],
16 | globals: {
17 | "use": "error",
18 | "window": "error",
19 | "document": true
20 | },
21 | rules: {
22 | "@typescript-eslint/camelcase": "off",
23 | "@typescript-eslint/no-explicit-any": "off",
24 | "@typescript-eslint/explicit-function-return-type": "off",
25 | "@typescript-eslint/indent": ["error", 4],
26 | "@typescript-eslint/no-empty-interface": "off",
27 | "@typescript-eslint/array-type": "off",
28 | "@typescript-eslint/explicit-member-accessibility": 0,
29 | "vue/html-indent": ["error", 4],
30 | "vue/html-indent": "off",
31 | "vue/max-attributes-per-line": "off",
32 | "camelcase": "off",
33 | "arrow-parens": ["error", "as-needed"],
34 | "import/extensions": "off",
35 | "lines-between-class-members": "off",
36 | "indent": ["error", 4],
37 | "class-methods-use-this": "off",
38 | "import/prefer-default-export": "off",
39 | "import/newline-after-import": "off",
40 | "global-require": "off",
41 | "prefer-template": "off",
42 | "max-len": ["error", 200],
43 | "no-console": "off",
44 | "no-debugger": "warn",
45 | "newline-per-chained-call": "off",
46 | "object-curly-newline": "off",
47 | "no-param-reassign": "off",
48 | "no-restricted-syntax": [
49 | "error",
50 | "LabeledStatement",
51 | "WithStatement"
52 | ],
53 | "no-underscore-dangle": "off",
54 | "import/no-unresolved": [
55 | "error",
56 | {
57 | "ignore": ['components/']
58 | }
59 | ]
60 | },
61 | settings: {
62 | 'import/resolver': {
63 | webpack: {
64 | config: path.join(__dirname, './compile/webpack.dev.js')
65 | }
66 | }
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 | npm-debug.log*
4 | node_modules/
5 | .npm
6 | build
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-template
2 |
3 | Vue 移动端单页面开发模板
4 |
5 | Webpack4 + Babel7 + Typescript + JSX
6 |
7 | - 代码检查:ESLint
8 | - 路由管理:Vue Route
9 | - 状态管理:Vuex
10 | - 移动端适配方案:Flexible rem
11 | - HTTP:Axios
12 |
13 | ## Usage
14 |
15 | ```
16 | git clone https://github.com/axuebin/vue-template.git
17 | ```
18 |
19 | ```
20 | cd vue-template
21 | npm install
22 | npm run dev
23 | npm run build
24 | ```
25 |
26 | ## 项目结构
27 |
28 | ```
29 | ├── compile // webpack 配置文件
30 | │ ├── webpack.common.js
31 | │ ├── webpack.dev.js
32 | │ └── webpack.prod.js
33 | ├── src
34 | │ ├── App.tsx
35 | │ ├── api // api 文件夹,管理请求相关
36 | │ │ ├── api.ts // api path
37 | │ │ └── service.ts // api service
38 | │ ├── assets // 静态文件
39 | │ │ ├── img // 静态图片
40 | │ │ └── style // 样式
41 | │ │ └── index.scss // 全局样式
42 | │ ├── components // 组件
43 | │ │ └── CommonTitle
44 | │ │ └── index.vue // vue 文件,不用 jsx 的写法
45 | │ ├── index.html // html 模板
46 | │ ├── index.ts // 入口文件
47 | │ ├── lib
48 | │ │ ├── axios.ts // 封装一下 axios
49 | │ │ └── utils.ts // 工具
50 | │ ├── route.ts // 路由管理
51 | │ ├── routes // 路由页面
52 | │ │ ├── index
53 | │ │ │ ├── index.scss
54 | │ │ │ └── index.tsx // jsx 写法
55 | │ │ └── test
56 | │ │ ├── index.scss
57 | │ │ └── index.tsx
58 | │ ├── store // store 文件夹
59 | │ │ ├── index.ts
60 | │ │ └── modules // 分 module 管理 store
61 | │ │ └── global.ts // 全局 store
62 | │ ├── types // 库定义
63 | │ │ ├── index.d.ts
64 | │ │ └── vue.d.ts
65 | │ └── mock // 本地接口 mock 文件
66 | │ ├── index.ts
67 | ├── package-lock.json
68 | ├── package.json
69 | ├── postcss.config.js // postcss 配置文件
70 | ├── tsconfig.json // ts 配置文件
71 | └── README.md
72 | ```
--------------------------------------------------------------------------------
/compile/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const { VueLoaderPlugin } = require('vue-loader');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
6 |
7 | const env = process.env.NODE_ENV;
8 | const chunkSorts = ['vendor', 'app'];
9 |
10 | const resolve = p => path.resolve(__dirname, p);
11 |
12 | function styleLoaders(lang) {
13 | const loaders = [{
14 | loader: 'css-loader'
15 | }, {
16 | loader: 'postcss-loader',
17 | options: {
18 | config: {
19 | path: resolve('postcss.config.js'),
20 | },
21 | },
22 | }]
23 | if (lang === 'scss') {
24 | loaders.push({
25 | loader: 'sass-loader',
26 | options: {
27 | indentedSyntax: false,
28 | },
29 | });
30 | }
31 | if (env === 'production') {
32 | return ExtractTextPlugin.extract({
33 | fallback: 'vue-style-loader',
34 | use: ['css-loader', 'sass-loader'],
35 | });
36 | }
37 | return ['vue-style-loader'].concat(loaders);
38 | }
39 |
40 |
41 | module.exports = {
42 | entry: {
43 | app: resolve('../src/index.ts'),
44 | },
45 | output: {
46 | path: resolve('../build'),
47 | filename: '[name].[hash:10].js',
48 | chunkFilename: '[id].[hash:10].js',
49 | },
50 | resolve: {
51 | extensions: ['.js', '.ts', '.tsx', '.vue', '.json'],
52 | alias: {
53 | '@': resolve('../src'),
54 | vue$: 'vue/dist/vue.esm.js',
55 | },
56 | },
57 | plugins: [
58 | new HtmlWebpackPlugin({
59 | template: resolve('../src/index.html'),
60 | filename: './index.html',
61 | excludeChunks: [],
62 | chunksSortMode: (a, b) => chunkSorts.indexOf(a.names[0]) - chunkSorts.indexOf(b.names[0]),
63 | minify: {
64 | collapseWhitespace: true,
65 | removeComments: true,
66 | },
67 | }),
68 | new VueLoaderPlugin(),
69 | new ExtractTextPlugin('[name].[hash:10].css'),
70 | new OptimizeCssAssetsPlugin({
71 | assetNameRegExp: /\.css$/g,
72 | cssProcessor: require('cssnano'),
73 | cssProcessorPluginOptions: {
74 | preset: ['default', { discardComments: { removeAll: true } }],
75 | },
76 | canPrint: true,
77 | })
78 | ],
79 | module: {
80 | rules: [
81 | { test: /\.vue$/, loader: 'vue-loader' },
82 | {
83 | test: /\.js$/,
84 | loader: 'babel-loader',
85 | exclude: /node_modules/,
86 | query: {
87 | cacheDirectory: true,
88 | },
89 | },
90 | {
91 | test: /\.ts$/,
92 | use: [
93 | {
94 | loader: 'babel-loader',
95 | },
96 | {
97 | loader: 'ts-loader',
98 | options: {
99 | appendTsSuffixTo: ['\\.vue$'],
100 | },
101 | },
102 | ],
103 | },
104 | {
105 | test: /\.tsx$/,
106 | exclude: /node_modules/,
107 | enforce: 'pre',
108 | use: [
109 | 'babel-loader',
110 | {
111 | loader: 'ts-loader',
112 | options: {
113 | appendTsSuffixTo: ['\\.vue$'],
114 | },
115 | },
116 | ],
117 | },
118 | { test: /\.css$/, use: styleLoaders() },
119 | { test: /\.scss$/, use: styleLoaders('scss') },
120 | {
121 | test: /\.(png|jpg|jpeg)$/,
122 | include: path.resolve(__dirname, '../src'),
123 | use: [
124 | {
125 | loader: 'url-loader',
126 | options: { limit: 20240 },
127 | },
128 | { loader: 'img-loader' },
129 | ],
130 | },
131 | ],
132 | },
133 | optimization: {
134 | splitChunks: {
135 | chunks: "all",
136 | },
137 | },
138 | };
139 |
--------------------------------------------------------------------------------
/compile/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const merge = require('webpack-merge');
4 | const common = require('./webpack.common.js');
5 |
6 | module.exports = merge(common, {
7 | mode: 'development',
8 | // devtool: 'inline-source-map',
9 | devServer: {
10 | contentBase: path.join(__dirname, '../src'),
11 | port: 7777,
12 | host: '0.0.0.0',
13 | hot: true,
14 | compress: true,
15 | },
16 | module: {
17 | rules: [{
18 | test: /\.(js|vue|ts|tsx)$/,
19 | loader: 'eslint-loader',
20 | exclude: /node_modules/,
21 | enforce: 'pre',
22 | }],
23 | },
24 | });
25 |
--------------------------------------------------------------------------------
/compile/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const merge = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 |
4 | module.exports = merge(common, {
5 | mode: 'production',
6 | });
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-template",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.ts",
6 | "types": "types/index.d.ts",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "build": "rimraf ./build/ && NODE_ENV=production webpack --config compile/webpack.prod.js",
10 | "dev": "NODE_ENV=development webpack-dev-server --config compile/webpack.dev.js"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "devDependencies": {
16 | "@babel/core": "^7.4.5",
17 | "@babel/plugin-proposal-class-properties": "^7.4.4",
18 | "@babel/plugin-proposal-decorators": "^7.4.4",
19 | "@babel/plugin-transform-runtime": "^7.4.4",
20 | "@babel/preset-env": "^7.4.5",
21 | "@babel/preset-stage-2": "^7.0.0",
22 | "@babel/runtime": "^7.4.5",
23 | "@typescript-eslint/eslint-plugin": "^1.10.2",
24 | "@typescript-eslint/parser": "^1.10.2",
25 | "babel-core": "^7.0.0-bridge.0",
26 | "babel-loader": "^7.1.5",
27 | "babel-plugin-syntax-jsx": "^6.18.0",
28 | "babel-plugin-transform-vue-jsx": "^3.7.0",
29 | "css-loader": "^3.0.0",
30 | "eslint": "^5.16.0",
31 | "eslint-config-airbnb-base": "^13.1.0",
32 | "eslint-import-resolver-webpack": "^0.11.1",
33 | "eslint-loader": "^2.1.2",
34 | "eslint-plugin-import": "^2.17.3",
35 | "eslint-plugin-vue": "^5.2.2",
36 | "extract-text-webpack-plugin": "^4.0.0-beta.0",
37 | "html-webpack-plugin": "^3.2.0",
38 | "node-sass": "^4.12.0",
39 | "optimize-css-assets-webpack-plugin": "^5.0.1",
40 | "postcss-loader": "^3.0.0",
41 | "regenerator-runtime": "^0.13.2",
42 | "sass-loader": "^7.1.0",
43 | "style-loader": "^0.23.1",
44 | "ts-loader": "^6.0.2",
45 | "typescript": "^3.5.2",
46 | "vue-loader": "^15.7.0",
47 | "vue-style-loader": "^4.1.2",
48 | "webpack": "^4.34.0",
49 | "webpack-cli": "^3.3.4",
50 | "webpack-dev-server": "^3.7.1",
51 | "webpack-merge": "^4.2.1"
52 | },
53 | "dependencies": {
54 | "autoprefixer": "^9.6.0",
55 | "axios": "^0.19.0",
56 | "axios-mock-adapter": "^1.16.0",
57 | "img-loader": "^3.0.1",
58 | "lib-flexible": "^0.3.2",
59 | "postcss-px2rem": "^0.3.0",
60 | "url-loader": "^2.0.0",
61 | "vue": "^2.6.10",
62 | "vue-class-component": "^7.1.0",
63 | "vue-property-decorator": "^8.2.1",
64 | "vue-router": "^3.0.6",
65 | "vue-template-compiler": "^2.6.10",
66 | "vuex": "^3.1.1",
67 | "vuex-class": "^0.3.2"
68 | },
69 | "browserslist": [
70 | "last 20 version"
71 | ]
72 | }
73 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const px2remConfigs = {
2 | baseDpr: 1,
3 | remUnit: 37.5,
4 | onePxComment: '1px',
5 | forcePxComment: '!px',
6 | keepComment: '!no',
7 | forcePxProperty: ['font-size'],
8 | };
9 |
10 | module.exports = {
11 | plugins: [
12 | require('autoprefixer')(),
13 | require('postcss-px2rem')(px2remConfigs),
14 | ],
15 | };
16 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { Component } from 'vue-property-decorator';
3 | import '@/assets/style/index.scss';
4 |
5 | @Component({})
6 | export default class App extends Vue {
7 | render() {
8 | return
9 |
10 |
;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/api/api.ts:
--------------------------------------------------------------------------------
1 | const base = 'https://api.axuebin.com';
2 |
3 | export const queryListPath = `${base}/blog/api/v1/list/query`;
4 |
--------------------------------------------------------------------------------
/src/api/service.ts:
--------------------------------------------------------------------------------
1 | import axios from '@/lib/axios';
2 | import {
3 | queryListPath,
4 | } from './api';
5 |
6 | export const queryList = async (payload = {}): Promise => {
7 | const response = await axios(queryListPath, payload, 'get');
8 | return response;
9 | };
10 |
--------------------------------------------------------------------------------
/src/assets/img/time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axuebin/mobile-vue-typescript-template/699364005d72892193fdffbf10473a5144b40fb2/src/assets/img/time.png
--------------------------------------------------------------------------------
/src/assets/style/index.scss:
--------------------------------------------------------------------------------
1 | body, html, #app {
2 | height: 100%;
3 | background-color: #F5F5F5;
4 | overflow-x: hidden;
5 | overflow-y: hidden;
6 | -webkit-overflow-scrolling: unset;
7 | }
8 |
9 | * {
10 | padding: 0;
11 | margin: 0;
12 | }
--------------------------------------------------------------------------------
/src/components/CommonTitle/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
15 |
22 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { ComponentOptions, CreateElement } from 'vue';
2 | import router from './route';
3 | import store from './store/index';
4 | import App from './App';
5 | import 'lib-flexible/flexible';
6 |
7 | if (process.env.NODE_ENV === 'development') {
8 | require('./mock');
9 | }
10 |
11 | /* eslint-disable */
12 | const vmOps: ComponentOptions = {
13 | el: '#app',
14 | router,
15 | store,
16 | render: (h: CreateElement) => h(App),
17 | }
18 | new Vue(vmOps);
19 |
--------------------------------------------------------------------------------
/src/lib/axios.ts:
--------------------------------------------------------------------------------
1 | import Axios from 'axios';
2 |
3 | // 每个请求都需要携带的公参
4 | const commonQuery = {};
5 |
6 | const axiosIns = Axios.create();
7 |
8 | axiosIns.interceptors.request.use(config => config, error => Promise.reject(error));
9 |
10 | const axios = (url, data: any = {}, method = 'get'): Promise => {
11 | const config: any = {};
12 | config.method = method;
13 | config.url = url;
14 | config.params = commonQuery;
15 | const { headers } = data;
16 | delete data.headers;
17 | if (method === 'get') {
18 | config.params = { ...config.params, ...data };
19 | } else {
20 | if (data.params) {
21 | config.params = { ...config.params, ...data.params };
22 | delete data.params;
23 | }
24 | config.data = data;
25 | }
26 |
27 | // 可能这里会有一些 promise
28 | return Promise.all([]).then(() => {
29 | // 鉴权方式自定义
30 | config.headers = {
31 | ...headers,
32 | };
33 | return axiosIns.request(config).then(response => {
34 | console.log(response.data);
35 | return response.data;
36 | }, error => {
37 | if (error.response && error.response.data && error.response.data.errors && error.response.data.errors.length > 0) {
38 | console.log(error.response.data.errors[0].message);
39 | } else {
40 | console.log('网络貌似有点问题,请稍后再试哦');
41 | }
42 | return Promise.reject(error);
43 | });
44 | });
45 | };
46 |
47 | export default axios;
48 |
49 | export { axiosIns };
50 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | export function qs() {
2 | const { href } = window.location;
3 | const result = {};
4 | let param = null;
5 | const reg = /[?&](.*?)=([^]*)/g;
6 | param = reg.exec(href);
7 | while (param) {
8 | try {
9 | result[param[1]] = decodeURIComponent(param[2]);
10 | } catch (e) {
11 | try {
12 | result[param[1]] = unescape(param[2]);
13 | } catch (escapeErr) {
14 | result[param[1]] = param[2]; // eslint-disable-line
15 | }
16 | }
17 | param = reg.exec(href);
18 | }
19 | return result;
20 | }
21 |
--------------------------------------------------------------------------------
/src/mock/index.ts:
--------------------------------------------------------------------------------
1 | import MockAdapter from 'axios-mock-adapter';
2 | import { axiosIns } from '@/lib/axios';
3 | import { queryListPath } from '@/api/api';
4 | const mock = new MockAdapter(axiosIns);
5 |
6 | mock.onGet(queryListPath).reply(200, {
7 | success: 0,
8 | data: [
9 | { id: 1, name: 'Vue 移动端单页面开发模板', user: 'axuebin' },
10 | { id: 2, name: 'typescipt vue 脚手架', user: 'xuebin' },
11 | ],
12 | });
13 |
--------------------------------------------------------------------------------
/src/route.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router, { RouteConfig } from 'vue-router';
3 | import App from './routes/index/index';
4 | import Test from './routes/test/index';
5 |
6 | Vue.use(Router);
7 |
8 | const routes: RouteConfig[] = [
9 | {
10 | path: '/',
11 | name: 'index',
12 | component: App,
13 | },
14 | {
15 | path: '/test',
16 | name: 'test',
17 | component: Test,
18 | },
19 | ];
20 |
21 | const router: Router = new Router({
22 | base: '/',
23 | routes,
24 | });
25 |
26 | export default router;
27 |
--------------------------------------------------------------------------------
/src/routes/index/index.scss:
--------------------------------------------------------------------------------
1 | .index {
2 | width: 375px;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | overflow-y: auto;
7 | -webkit-overflow-scrolling: touch;
8 | background-color: #FFFFFF;
9 | .msg {
10 | margin-top: 12px;
11 | font-size: 14px;
12 | color: #FF5151;
13 | display: flex;
14 | align-items: center;
15 | .time-img {
16 | width: 16px;
17 | height: 16px;
18 | margin-right: 4px;
19 | }
20 | }
21 | .btn {
22 | margin-top: 12px;
23 | padding: 0 12px;
24 | height: 30px;
25 | font-size: 14px;
26 | text-align: center;
27 | line-height: 30px;
28 | background-color: #FF5151;
29 | color: #FFFFFF;
30 | border-radius: 4px;
31 | }
32 | .vuex-test {
33 | margin-top: 12px;
34 | display: flex;
35 | align-items: center;
36 | .count {
37 | font-size: 16px;
38 | }
39 | &-btn {
40 | padding: 0 12px;
41 | height: 30px;
42 | margin-left: 24px;
43 | border: 1px solid #E5E5E5; /*!no*/
44 | border-radius: 4px;
45 | text-align: center;
46 | line-height: 30px;
47 | }
48 | }
49 | .list {
50 | margin-top: 12px;
51 | &-item {
52 | margin-top: 6px;
53 | & > span {
54 | margin-left: 12px;
55 | }
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/routes/index/index.tsx:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { namespace } from 'vuex-class';
3 | import { Component } from 'vue-property-decorator';
4 | import CommonTitle from '@/components/CommonTitle/index.vue';
5 | import { Me as GlobalMe } from '@/store/modules/global';
6 | import { queryList } from '@/api/service';
7 | import timeImg from '@/assets/img/time.png';
8 | import '@/assets/style/index.scss';
9 | import './index.scss';
10 |
11 | const globalModule = namespace('global');
12 |
13 | interface ListItem {
14 | id: number;
15 | name: string;
16 | user: string;
17 | }
18 |
19 | @Component({
20 | components: {
21 | CommonTitle,
22 | },
23 | })
24 | export default class Index extends Vue {
25 | @globalModule.State('count') count: number;
26 | @globalModule.State('me') me: GlobalMe;
27 | @globalModule.Action('increment') onIncrement: Function;
28 | title: string = '首页';
29 | msg: string = 'hello world';
30 | list: ListItem[] = [];
31 | mounted() {
32 | this.queryList();
33 | console.log(this.me.name);
34 | }
35 | async queryList() {
36 | await queryList().then(res => {
37 | if (res.success === 0) {
38 | this.list = res.data;
39 | }
40 | }).catch(err => {
41 | console.log('err', err);
42 | });
43 | }
44 | routerPushTest() {
45 | this.$router.push({ path: 'test', query: { name: 'axuebin' } });
46 | }
47 | onClickCountIncrement() {
48 | this.onIncrement();
49 | }
50 | render() {
51 | const { title, msg, count, list } = this;
52 | return
53 |
54 |
{msg}
55 |
路由跳转测试页面
56 |
57 |
count: {count}
58 |
count +
59 |
60 | {
61 | list.length > 0
62 | ?
63 | {
64 | list.map(item =>
65 | {item.id}
66 | {item.name}
67 | {item.user}
68 |
)
69 | }
70 |
71 | : ''
72 | }
73 |
;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/routes/test/index.scss:
--------------------------------------------------------------------------------
1 | .test {
2 | width: 375px;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | overflow-y: auto;
7 | -webkit-overflow-scrolling: touch;
8 | background-color: #FFFFFF;
9 | .msg {
10 | margin-top: 12px;
11 | font-size: 14px;
12 | color: #FF5151;
13 | }
14 | .btn {
15 | margin-top: 12px;
16 | padding: 0 12px;
17 | height: 30px;
18 | font-size: 14px;
19 | text-align: center;
20 | line-height: 30px;
21 | background-color: #FF5151;
22 | color: #FFFFFF;
23 | border-radius: 4px;
24 | }
25 | .vuex-test {
26 | .count {
27 | margin-top: 12px;
28 | font-size: 16px;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/routes/test/index.tsx:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { namespace } from 'vuex-class';
3 | import { Component } from 'vue-property-decorator';
4 | import CommonTitle from '@/components/CommonTitle/index.vue';
5 | import { qs } from '@/lib/utils';
6 | import '@/assets/style/index.scss';
7 | import './index.scss';
8 |
9 | const globalModule = namespace('global');
10 |
11 | @Component({
12 | components: {
13 | CommonTitle,
14 | },
15 | })
16 | export default class Index extends Vue {
17 | @globalModule.State('count') count: number;
18 | title: string = '测试';
19 | msg: string = 'hello world';
20 | mounted() {
21 | console.log(qs());
22 | }
23 | routerPushTest() {
24 | this.$router.push('/');
25 | }
26 | render() {
27 | const { title, msg, count } = this;
28 | return
29 |
30 |
{msg}
31 |
路由跳转首页
32 |
35 |
;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 | import GlobalModule from './modules/global';
4 |
5 | Vue.use(Vuex);
6 |
7 | const store = new Vuex.Store({
8 | modules: {
9 | global: GlobalModule,
10 | },
11 | });
12 |
13 | export default store;
14 |
--------------------------------------------------------------------------------
/src/store/modules/global.ts:
--------------------------------------------------------------------------------
1 | import { Commit } from 'vuex';
2 |
3 | export interface Me {
4 | name: string;
5 | age: number;
6 | }
7 |
8 | export interface GlobalState {
9 | count: number;
10 | me: Me;
11 | }
12 |
13 | const state: GlobalState = {
14 | count: 0,
15 | me: {
16 | name: 'axuebin',
17 | age: 1,
18 | },
19 | };
20 |
21 | const mutations: any = {
22 | increment(states: any) {
23 | states.count += 1;
24 | },
25 | };
26 |
27 | const actions: any = {
28 | increment(context: { commit: Commit }) {
29 | context.commit('increment');
30 | },
31 | };
32 |
33 | export default {
34 | namespaced: true,
35 | state,
36 | mutations,
37 | actions,
38 | };
39 |
--------------------------------------------------------------------------------
/src/types/images.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png';
2 | declare module '*.jpg';
3 | declare module '*.jpeg';
4 | declare module '*.gif';
5 |
--------------------------------------------------------------------------------
/src/types/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*vue' {
2 | import Vue from 'vue/dist/vue.esm.js';
3 | export default Vue;
4 | }
5 |
6 | // declare module '*.scss' {
7 | // const content: {[className: string]: string}
8 | // export default content
9 | // }
10 |
--------------------------------------------------------------------------------
/src/types/vue.d.ts:
--------------------------------------------------------------------------------
1 | import Vue, { ComponentOptions, VNode } from 'vue';
2 |
3 | declare module 'vue/types/options' {
4 | interface ComponentOptions {
5 | app?: any;
6 | }
7 | }
8 |
9 | declare global {
10 | namespace JSX {
11 | interface Element extends VNode { }
12 | interface ElementClass extends Vue { }
13 | interface IntrinsicElements {
14 | [elem: string]: any;
15 | }
16 | }
17 | }
18 |
19 | declare module 'vue/types/vue' {
20 | interface Vue {
21 | $app: any;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "preserve",
4 | "jsxFactory": "h",
5 | "allowSyntheticDefaultImports": true,
6 | "experimentalDecorators": true,
7 | "strictFunctionTypes": false,
8 | "sourceMap": true,
9 | "module": "ESNext",
10 | "moduleResolution": "node",
11 | "target": "ES2016",
12 | "lib": [
13 | "es2017",
14 | "es2015",
15 | "es2015.promise",
16 | "dom",
17 | ],
18 | "typeRoots": [
19 | "./node_modules/@types",
20 | "./types"
21 | ],
22 | "baseUrl": ".",
23 | "paths": {
24 | "vue": ["node_modules/vue/types"],
25 | "@/*": ["src/*"],
26 | }
27 | },
28 | "include": [
29 | "src/**/*"
30 | ],
31 | "exclude": [
32 | "node_modules",
33 | "compile",
34 | ],
35 | }
36 |
--------------------------------------------------------------------------------