记录搭建 Taro Next + Dva + TS 项目(一:踩坑指南)

4,105 阅读1分钟

前言

🥬🐔最近忙着准备面试,恶补基础原理好久都没敲代码,项目的BUG也好久没修了噗。然后项目组又有的一个新的需求涉及到小程序,所以就打算开始弄一个平台的小程序端,前期定位的话是作为一个辅助支撑教学的方式吧,如果后续有时间和需求会继续完善。

好久没有写Taro了,有啥地方写的不对的欢迎大家指出来!还有 TS 😂用的还不是很熟练....

现在来记录一下搭建(踩 坑)过程吧

目前暂时计划实现如下功能:

  • 账号
  • 比赛报名、进度查询
  • 课程、课堂
  • 题库
  • 讨论
  • ...

WEB项目线上地址: oj.bnuz.edu.cn

项目技术栈

  • Taro Next (当踩坑了)
  • DvaJs (用习惯了)
  • TypeScript (大势所趋)
  • Taro-UI (方便😂)

啥也不说了 开踩

项目搭建

新建项目

首先安装新的 Taro-cli

需要使用 npm 或者 yarn 全局安装 @tarojs/cli,或者直接使用 npx:

# 使用 npm 安装 CLI
$ npm install -g @tarojs/cli@next
# OR 使用 yarn 安装 CLI
$ yarn global add @tarojs/cli@next
# OR 安装了 cnpm,使用 cnpm 安装 CLI
$ cnpm install -g @tarojs/cli@next

在这里我踩了个坑,如果之前是有安装过 Taro-Cli 的 这里建议使用 cnpm 去安装 taro cli@next,我用 npm 安装后,虽然成功了,但是实际上 cli 的版本还是 2.0,在 issue 找到说可以使用 cnpm 解决,试了后果然可以...... 还有一些具体的迁移问题可以看这里Taro Next 迁移指南

PS:一开始没好好看文档,没看到@taro-redux 不会再维护,哎...这里卡了贼久,所以这里需要安装react-redux的依赖。

安装好后运行 taro init xxxxxx xxxxxx 为项目名。 在这里的话会通过 git 拉取模版啥的,还是挺慢的。
配置方面的话,这里的话就按照个人习惯去选择就好了,我的话就选择 Ts + Sass + 默认模版,然后再等个几分钟就好了。

依赖安装

在终端的项目根目录下安装如下依赖:

  • DVA npm install --save dva-core dva-loading

  • Redux npm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger

  • Taro-UI npm install taro-ui

配置项目文件

项目结构

src目录下

  • assets:静态资源
  • components:通用组件
  • pages:页面文件目录
  • constants:一些常量
  • models:DVA的 models
  • services
  • utils:基础工具类

配置完之后

src
├── app.config.ts
├── app.scss
├── app.ts
├── assets
├── constants
├── index.html
├── mock
├── models
├── pages
│   └── index
│       ├── index.config.ts
│       ├── index.scss
│       └── index.tsx
├── services
└── utils

DVA

src/utils下创建dva.ts

import { create } from "dva-core";
import { createLogger } from "redux-logger";
import  createLoading  from "dva-loading";



let app
let store
let dispatch
let registered

function createApp(opt) {
    // redux 的日志
    opt.onAction = [createLogger()]
    app = create(opt)
    app.use(createLoading({}))

    if (!registered) {
        opt.models.forEach(model => app.model(model));
    }
    registered = true;
    app.start()

    store = app._store;
    app.getStore = () => store;
    app.use({
        onError(err){
            console.log(err);
        }
    })

    dispatch = store.dispatch;
    app.dispatch = dispatch;
    return app;
}

export default{
    createApp,
    getDispatch(){
        return app.dispatch
    }
}

src/models 下创建 index.ts 用来导出所有的 model 这里我是把 model 全部集中到 models 里面,也可以把 model 放在页面文件夹下,根据个人习惯。

import global from './global';

export default [
  // 放导出的models
  global,
]

根据你的习惯定义好 model,这里是 src/models/global.ts

import { Subscription, Effect } from 'dva';
import { Reducer} from "redux";

export interface GlobalModelState {
  // 定义state
}

export interface GlobalModelType {
  namespace: 'global',
  state: GlobalModelState,
  effects: {
    // xxxxx: Effect xxxx是effect的名字
  },
  reducers: {
    // xxxx: Reducer<GlobalModelState>
  };
  subscriptions: {
    //xxx: Subscription
  }
}

const GlobalModel: GlobalModelType = {
  namespace: 'global',
  state: {

  },
  effects: {
    // effect函数
  },
  subscriptions: {
    // 订阅
  },
  reducers: {
    // reducer
  }
};

export default GlobalModel;

在入口文件 app.tsx(这里将 app.ts 改成 app.tsx)导入 DVA

import React, { Component } from 'react'

import { Provider} from "@tarojs/redux";
import dva from './utils/dva';

import models from './models';

import './app.scss'

const dvaApp = dva.createApp({
  initialState: {},
  models: models,
});

const store = dvaApp.getStore();

class App extends Component {

  componentDidMount () {}

  componentDidShow () {}

  componentDidHide () {}

  componentDidCatchError () {}

  // 在Taro Next中 config改为外部文件配置 在.config.ts

  // this.props.children 是将要会渲染的页面
  render () {
    return (
    // 注入dva
      <Provider store={store}>
        {this.props.children}
      </Provider>
    )
  }
}

export default App

然后就可以愉快的npm run dev:weapp试试了

简单的例子

但也感知不到 dva 的存在,现在我们写一个简单的例子 首先在 models/global

export interface GlobalModelState {
  name: string, // 增加name定义
}

export interface GlobalModelType {
  namespace: 'global',
  state: GlobalModelState,
  effects: {
    changeName: Effect, // 增加changeName的Effect
  },
  reducers: {
    saveName: Reducer, // 增加saveName的Reducer
  };
  subscriptions: {
    //xxx: Subscription
  }
}


effects: {
    *changeName({ payload }, { put, call  }) {
      const response = yield call(fakeGlobalChangeName, payload); // 这里调用services层
      yield put({
        type: 'saveName',
        payload: {
          name: payload.name,
        }
      })
    }
  },

reducers: {
    // reducer
    saveName(state, { payload }) {
      return {
        ...state,
        name: payload.name
      }
    }
  }
  

然后在 services下创建global.ts

export interface ChangeNameParamsType {
  name: string | number,
}

export async function fakeGlobalChangeName(params: ChangeNameParamsType) {
  return Promise.resolve(params.name + '' + Math.random() * 1000 )
  // 请异步API请求
}

回到index.tsx就应该

import React, { useEffect } from 'react';
import {Button, View} from "@tarojs/components";

import { connect } from 'react-redux';
import './index.scss'
import {ConnectState, GlobalModelState} from "../../models/connect";
import {Dispatch,AnyAction} from "redux";

interface IndexPropsType {
  global: GlobalModelState,
  dispatch: Dispatch<AnyAction>,
}

const Index: React.FC<IndexPropsType> = props => {
  const { dispatch, global} = props;

  useEffect(() => {
    // componentDidMount

  }, []);

  const handleChangeName = () => {
    dispatch({
      type: 'global/changeName',
      payload: {
        name: 'hei',
      }
    })
  };

  return <View>
    {global.name}
    <Button onClick={handleChangeName}>点击</Button>
  </View>
}

export default connect(({ global }: ConnectState) => ({
  global
}))(Index);

打开console就可以看到整个的 Dva 是怎么走的

这样就测试了一个简单的 dva 数据流的流程

这个例子只是为了走个流程,实际上肯定不是啥都要走 DVA(HOOKS不香吗

到这里的话基本上雏形已经出来了,但是一些还缺少了各种基础的工具类、路径的别名、request的封装,logger、常量、项目config、mock、基本组件封装.......一堆东西。

但上面那些的东西话 100 个人有 100 种写法 ... 嗯 ... 对 (肯定不是我懒得写下去了

一些常用的基本库啥的下次再记录吧~

踩坑合集

首先真的要去看一下

Taro Next 的迁移指南!!Taro Next 的迁移指南!!Taro Next 的迁移指南!!

去过一遍的话真的能少踩很多坑😢

  • redux 等第三方 React 库的依赖变了
  • 属于框架本身的 API 由框架的包引入
  • TaroCli 和 Taro 要注意版本
  • 会有一个新的文件:.config.js , 代表你页面/项目文件的文件名,config 文件必须和页面/项目文件在同一文件夹
  • 路由的获取方式变了
  • 注意依赖的更新!!
  • 生命周期改名
  • 有自带的HOOKS
  • 多看文档 多看文档 多看文档

Taro-UI

关于Taro-UI 在引入全局 scss 的时候会报路径找不到,关于这个问题issue里面也有,这个属于还没适配Taro Next吧。然后 Taro 的交流群也给出了解决方案

来自Taro开发交流群@Goodman

taro next的taro-ui 问题,临时修复方法
package.json更新依赖:taro-ui: NervJS/taro-ui#next

{
"dependencies": { "taro-ui": "NervJS/taro-ui#next" }
}
复制 cp -r ./node_modules/taro-ui/src ./src/taro-ui
修改 config/index.js

const path = require('path')
const config = {
  alias: {
    "taro-ui": path.resolve(__dirname, '../src/taro-ui'),
    "nervjs": path.resolve(__dirname, '../node_modules/react')
  },
}
我试了一下,确实是可以用的,不过AtCalendar这个组件报

Module not found: Can't resolve 'dayjs' in 'src/taro-ui/components/calendar'
暂时先注释掉了这个组件

执行 npm i dayjs 就可以了

上面是社区的解决方案,我自己的话就没有把taro-ui搬出来采用的是以下

package.json更新依赖:taro-ui: NervJS/taro-ui#next

config/index.js
const config = {
  alias: {
    "taro-ui": path.resolve(__dirname, '../node_modules/taro-ui/src'),
    "nervjs": path.resolve(__dirname, '../node_modules/react')
  },
  ...
}

因为我用了自定义的主题
所以定义了custom-theme.scss
/* Custom Theme */
$color-brand: #52c41a !default;
$color-brand-light: #7dd353 !default;
$color-brand-dark: #429d15 !default;
/*Taro UI 默认样式*/
@import 'taro-ui/style/index.scss';

然后再app.tsx引入scss就好了
TaroUi的组件还是import from 'taro-ui'

上面就是一个临时的解决办法,希望官方大大早点适配吧!! 溜了溜了 还要去复习

剩下的下次再记录吧

哪里写的不妥的希望各位大佬们指出!!

官方文档

Taro Next 迁移指南

Taro Next 官方文档