├── .editorconfig
├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── config
├── dev.js
├── index.js
└── prod.js
├── global.d.ts
├── package.json
├── project.config.json
├── src
├── app.less
├── app.tsx
├── index.html
├── models
│ ├── home.tsx
│ └── index.tsx
├── pages
│ └── index
│ │ ├── index.less
│ │ └── index.tsx
├── services
│ └── home.tsx
└── utils
│ ├── dva.tsx
│ └── storage.tsx
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["taro"],
3 | "rules": {
4 | "no-unused-vars": ["error", { "varsIgnorePattern": "Taro" }],
5 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }],
6 | "import/prefer-default-export": 0
7 | },
8 | "parser": "@typescript-eslint/parser",
9 | "parserOptions": {
10 | "ecmaFeatures": {
11 | "jsx": true
12 | },
13 | "useJSXTextNode": true,
14 | "project": "./tsconfig.json"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | .temp/
3 | .rn_temp/
4 | node_modules/
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Yanghc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # taro-dva-demo
2 |
3 | [](https://github.com/NervJS/taro/)
4 | [](https://dvajs.com/)
5 |
6 | 一个基于 React + Taro + Dva构建的适配不同端(微信/百度/支付宝小程序、H5、React-Native 等)的小程序DEMO
7 |
8 |
9 | ## 特性
10 |
11 | #### Taro特性
12 |
13 | * **React 语法风格**,Taro 的语法规则基于 React 规范,它采用与 React 一致的组件化思想,组件生命周期与 React 保持一致,同时在书写体验上也尽量与 React 类似,支持使用 JSX 语法,让代码具有更丰富的表现力。
14 |
15 | #### Dva 特性
16 | * **易学易用**,仅有 6 个 api,对 redux 用户尤其友好,**[配合 umi 使用](https://umijs.org/guide/with-dva.html)后更是降低为 0 API**
17 | * **elm 概念**,通过 reducers, effects 和 subscriptions 组织 model
18 | * **插件机制**,比如 [dva-loading](https://github.com/dvajs/dva/tree/master/packages/dva-loading) 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading
19 | * **支持 HMR**,基于 [babel-plugin-dva-hmr](https://github.com/dvajs/babel-plugin-dva-hmr) 实现 components、routes 和 models 的 HMR
20 |
21 |
22 | ## 使用
23 |
24 | ```
25 |
26 | git clone https://github.com/hqwlkj/taro-dva-demo.git my-weapp
27 |
28 | cd my-weapp
29 |
30 | # 全局安装taro脚手架
31 |
32 | 使用 npm 安装 CLI
33 | $ npm install -g @tarojs/cli
34 |
35 | OR 使用 yarn 安装 CLI
36 | $ yarn global add @tarojs/cli
37 |
38 | OR 安装了 cnpm,使用 cnpm 安装 CLI
39 | $ cnpm install -g @tarojs/cli
40 |
41 | # 安装项目依赖
42 | 使用 yarn 安装依赖
43 | $ yarn
44 |
45 | OR 使用 cnpm 安装依赖
46 | $ cnpm install
47 |
48 | OR 使用 npm 安装依赖
49 | $ npm install
50 |
51 | # 微信小程序
52 |
53 | yarn
54 | $ yarn dev:weapp
55 | $ yarn build:weapp
56 |
57 | npm script
58 | $ npm run dev:weapp
59 | $ npm run build:weapp
60 |
61 | 仅限全局安装
62 | $ taro build --type weapp --watch
63 | $ taro build --type weapp
64 |
65 | npx 用户也可以使用
66 | $ npx taro build --type weapp --watch
67 | $ npx taro build --type weapp
68 |
69 | ```
70 | 更多使用方式请参考[Taro 官方使用说明](http://taro-docs.jd.com/taro/docs/GETTING-STARTED.html)
71 |
72 | ## 参与贡献
73 |
74 | 我们非常欢迎你的贡献,你可以通过以下方式和我一起共建 :smiley::
75 |
76 | - 通过 [Issue](https://github.com/hqwlkj/taro-dva-demo/issues) 报告 bug 或进行咨询。
77 | - 提交 [Pull Request](https://github.com/hqwlkj/taro-dva-demo/pulls) 改进代码。
78 |
--------------------------------------------------------------------------------
/config/dev.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"development"'
4 | },
5 | defineConstants: {
6 | },
7 | weapp: {},
8 | h5: {}
9 | }
10 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | projectName: 'weapp',
3 | date: '2019-8-18',
4 | designWidth: 750,
5 | deviceRatio: {
6 | '640': 2.34 / 2,
7 | '750': 1,
8 | '828': 1.81 / 2
9 | },
10 | sourceRoot: 'src',
11 | outputRoot: 'dist',
12 | plugins: {
13 | babel: {
14 | sourceMap: true,
15 | presets: [
16 | ['env', {
17 | modules: false
18 | }]
19 | ],
20 | plugins: [
21 | 'transform-decorators-legacy',
22 | 'transform-class-properties',
23 | 'transform-object-rest-spread'
24 | ]
25 | }
26 | },
27 | defineConstants: {
28 | },
29 | copy: {
30 | patterns: [
31 | ],
32 | options: {
33 | }
34 | },
35 | weapp: {
36 | module: {
37 | postcss: {
38 | autoprefixer: {
39 | enable: true,
40 | config: {
41 | browsers: [
42 | 'last 3 versions',
43 | 'Android >= 4.1',
44 | 'ios >= 8'
45 | ]
46 | }
47 | },
48 | pxtransform: {
49 | enable: true,
50 | config: {
51 |
52 | }
53 | },
54 | url: {
55 | enable: true,
56 | config: {
57 | limit: 10240 // 设定转换尺寸上限
58 | }
59 | },
60 | cssModules: {
61 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
62 | config: {
63 | namingPattern: 'module', // 转换模式,取值为 global/module
64 | generateScopedName: '[name]__[local]___[hash:base64:5]'
65 | }
66 | }
67 | }
68 | }
69 | },
70 | h5: {
71 | publicPath: '/',
72 | staticDirectory: 'static',
73 | module: {
74 | postcss: {
75 | autoprefixer: {
76 | enable: true,
77 | config: {
78 | browsers: [
79 | 'last 3 versions',
80 | 'Android >= 4.1',
81 | 'ios >= 8'
82 | ]
83 | }
84 | },
85 | cssModules: {
86 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
87 | config: {
88 | namingPattern: 'module', // 转换模式,取值为 global/module
89 | generateScopedName: '[name]__[local]___[hash:base64:5]'
90 | }
91 | }
92 | }
93 | }
94 | }
95 | }
96 |
97 | module.exports = function (merge) {
98 | if (process.env.NODE_ENV === 'development') {
99 | return merge({}, config, require('./dev'))
100 | }
101 | return merge({}, config, require('./prod'))
102 | }
103 |
--------------------------------------------------------------------------------
/config/prod.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"production"'
4 | },
5 | defineConstants: {
6 | },
7 | weapp: {},
8 | h5: {
9 | /**
10 | * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。
11 | * 参考代码如下:
12 | * webpackChain (chain) {
13 | * chain.plugin('analyzer')
14 | * .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
15 | * }
16 | */
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.png";
2 | declare module "*.gif";
3 | declare module "*.jpg";
4 | declare module "*.jpeg";
5 | declare module "*.svg";
6 | declare module "*.css";
7 | declare module "*.less";
8 | declare module "*.scss";
9 | declare module "*.sass";
10 | declare module "*.styl";
11 |
12 | // @ts-ignore
13 | // eslint-disable-next-line no-unused-vars
14 | declare const process: {
15 | env: {
16 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt';
17 | [key: string]: any;
18 | }
19 | }
20 |
21 | // eslint-disable-next-line no-unused-vars
22 | declare const global: any | {};
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "weapp",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "粮果惠社区优选微信小程序、支付宝小程序、h5app、RNApp等",
6 | "scripts": {
7 | "build:weapp": "taro build --type weapp",
8 | "build:swan": "taro build --type swan",
9 | "build:alipay": "taro build --type alipay",
10 | "build:tt": "taro build --type tt",
11 | "build:h5": "taro build --type h5",
12 | "build:rn": "taro build --type rn",
13 | "dev:weapp": "npm run build:weapp -- --watch",
14 | "dev:swan": "npm run build:swan -- --watch",
15 | "dev:alipay": "npm run build:alipay -- --watch",
16 | "dev:tt": "npm run build:tt -- --watch",
17 | "dev:h5": "npm run build:h5 -- --watch",
18 | "dev:rn": "npm run build:rn -- --watch"
19 | },
20 | "author": "",
21 | "license": "MIT",
22 | "dependencies": {
23 | "@tarojs/async-await": "1.3.13",
24 | "@tarojs/components": "1.3.13",
25 | "@tarojs/redux": "1.3.13",
26 | "@tarojs/redux-h5": "1.3.13",
27 | "@tarojs/router": "1.3.13",
28 | "@tarojs/taro": "1.3.13",
29 | "@tarojs/taro-alipay": "1.3.13",
30 | "@tarojs/taro-h5": "1.3.13",
31 | "@tarojs/taro-swan": "1.3.13",
32 | "@tarojs/taro-tt": "1.3.13",
33 | "@tarojs/taro-weapp": "1.3.13",
34 | "dva-core": "^1.4.0",
35 | "dva-loading": "^3.0.12",
36 | "nerv-devtools": "^1.3.9",
37 | "nervjs": "^1.3.9",
38 | "react": "^16.9.0",
39 | "react-dom": "^16.9.0",
40 | "redux": "^4.0.4",
41 | "redux-logger": "^3.0.6",
42 | "redux-thunk": "^2.3.0"
43 | },
44 | "devDependencies": {
45 | "@tarojs/plugin-babel": "1.3.13",
46 | "@tarojs/plugin-csso": "1.3.13",
47 | "@tarojs/plugin-less": "1.3.13",
48 | "@tarojs/plugin-uglifyjs": "1.3.13",
49 | "@tarojs/webpack-runner": "1.3.13",
50 | "@types/react": "^16.4.6",
51 | "@types/webpack-env": "^1.13.6",
52 | "@typescript-eslint/parser": "^1.6.0",
53 | "babel-eslint": "^8.2.3",
54 | "babel-plugin-transform-class-properties": "^6.24.1",
55 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
56 | "babel-plugin-transform-jsx-stylesheet": "^0.6.5",
57 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
58 | "babel-preset-env": "^1.6.1",
59 | "eslint": "^4.19.1",
60 | "eslint-config-taro": "1.3.13",
61 | "eslint-plugin-import": "^2.12.0",
62 | "eslint-plugin-react": "^7.8.2",
63 | "eslint-plugin-react-hooks": "^1.7.0",
64 | "eslint-plugin-taro": "1.3.13",
65 | "typescript": "^3.0.1"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "miniprogramRoot": "dist/",
3 | "projectname": "weapp",
4 | "description": "taro-dva-demo",
5 | "appid": "touristappid",
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": false,
9 | "postcss": false,
10 | "minified": false
11 | },
12 | "compileType": "miniprogram",
13 | "simulatorType": "wechat",
14 | "simulatorPluginLibVersion": {},
15 | "condition": {}
16 | }
17 |
--------------------------------------------------------------------------------
/src/app.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hqwlkj/taro-dva-demo/afc32b778109634f0356b790804f40c482afe8c6/src/app.less
--------------------------------------------------------------------------------
/src/app.tsx:
--------------------------------------------------------------------------------
1 | import '@tarojs/async-await';
2 | import Taro, { Component, Config as TaroConfig } from '@tarojs/taro';
3 | import { Provider } from '@tarojs/redux';
4 | import Index from './pages/index';
5 | import dva from './utils/dva';
6 | import storage from './utils/storage';
7 | import models from './models';
8 |
9 | import './app.less'
10 |
11 | // 如果需要在 h5 环境中开启 React Devtools
12 | // 取消以下注释:
13 | // if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5') {
14 | // require('nerv-devtools')
15 | // }
16 |
17 | const dvaApp = dva.createApp({
18 | initialState: {},
19 | models: models
20 | });
21 |
22 | const stores = dvaApp.getStore();
23 |
24 | class App extends Component {
25 |
26 | /**
27 | * 指定config的类型声明为: Taro.Config
28 | *
29 | * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型
30 | * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string
31 | * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型
32 | */
33 | config: TaroConfig = {
34 | pages: [
35 | 'pages/index/index'
36 | ],
37 | window: {
38 | backgroundTextStyle: 'light',
39 | navigationBarBackgroundColor: '#fff',
40 | navigationBarTitleText: 'WeChat',
41 | navigationBarTextStyle: 'black'
42 | },
43 | networkTimeout: {
44 | request: 10000,
45 | uploadFile: 10000
46 | }
47 | };
48 |
49 | private timer;
50 | private jumped = false;
51 |
52 | componentWillMount(): void {
53 | let host: string = ''; // 服务器地址
54 | if (process.env.NODE_ENV === 'development') {
55 | host = 'http://loaclhost:8080/'; // 测试环境服务器地址
56 | } else {
57 | host = 'http://127.0.0.1:8080/'; // 生产环境服务器地址
58 | }
59 |
60 | const interceptor = chain => {
61 | const requestParams = chain.requestParams;
62 | if (!/^(http|\/\/)/.test(requestParams.url)) {
63 | requestParams.url = host + requestParams.url;
64 | }
65 | const token = storage.get('token');
66 | if (token) {
67 | if (!requestParams.header) {
68 | requestParams.header = {};
69 | }
70 | requestParams.header.Authorization = `Bearer ${token}`;
71 | }
72 | return chain.proceed(requestParams).then(res => {
73 | const {
74 | statusCode,
75 | data: { message = '网络请求失败' }
76 | } = res;
77 | if (statusCode === 401 || statusCode === 403) {
78 | clearTimeout(this.timer);
79 | this.timer = setTimeout(() => {
80 | this.jumped = false;
81 | }, 1000);
82 | if (this.jumped) {
83 | return;
84 | }
85 | this.jumped = true;
86 | Taro.navigateTo({
87 | url: `/pages/login/index`
88 | }).then(() =>
89 | Taro.showToast({
90 | title: '请登录',
91 | icon: 'none'
92 | })
93 | );
94 | }
95 | if (statusCode >= 400) {
96 | Taro.showToast({
97 | title: message,
98 | icon: 'none'
99 | });
100 | return Promise.reject(res);
101 | }
102 | return res;
103 | });
104 | };
105 |
106 | Taro.addInterceptor(interceptor);
107 | }
108 |
109 | componentDidMount() {
110 | }
111 |
112 | componentDidShow() {
113 | }
114 |
115 | componentDidHide() {
116 | }
117 |
118 | componentDidCatchError() {
119 | }
120 |
121 | // 在 App 类中的 render() 函数没有实际作用
122 | // 请勿修改此函数
123 | render() {
124 | return (
125 |