起因
最近在工作上发现一个问题,我的开发框架很混乱。
我刚加入公司的时候,公司主要产品有两大类,一类是使用 Vue 开发。另一类则比较古老,前后端未分离,使用 jQuery 开发。而一些周期很短,或非核心业务的项目,比如给集团子公司和兄弟公司开发的公众号,或者管理类后台系统等待。则又有两种情况。第一种是样式较复杂或者交互逻辑较复杂的,由专业的前端人员开发。第二种是样式统一或交互简单的,由后端人员一并开发。如果是由前端主导开发,我比较偏向使用 React 和 TypeScript。如果是由后端主导开发,他们会使用一套由已经不在公司任职的前端同事基于 Vue 封装的框架,但此框架会有一些缺点,第一文档不全,第二无人维护。
基于上述情况,我大致梳理出目前所有用到的框架:
- Vue
- jQuery
- React
- React+TypeScript
这样造成的结果就是维护成本会很高。因为技术栈过于庞杂。
观点
关于前端的开发语言和开发框架,每个团队和每个人都有各自的看法和观点。我的观点就是:**绝大部分情况的项目,使用 React+TypeScript 的组合都是很完美的选择。**但不包括一些特殊的项目,比如对性能要求过高或者过于简单的项目,比如游戏项目,或单纯两三个纯展示页面的项目,这类情况,可以选择使用纯 ES5 开发。
基于以上观点,我还有一个新的观点:能够用 Vue 或者 jQuery 的项目,用 React 同样可以胜任,甚至可以做的更好。
这两条是我关于 React 的看法。我并没有认为 Vue 不够优秀或者 jQuery 已经过时了,我也不会拿一堆数据来证明 React 是最好的前端开发框架。争论这些毫无意义,适合自己的才是对你而言最好的。
关于 TypeScript 的观点,首先我是十分支持的,从 16 年它面世以来我就在关注和学习它了。并且我认为:**能够用 JavaScript 开发的项目,用 TypeScript 同样可以胜任,甚至可以做的更好。**同样,我也不会争论 TypeScript 和 JavaScript 哪个更好。
目标
最后我决定利用空闲时间,基于 React 和 TypeScript,搭建一套开发模板。
搭建模板的目的很简单,我不会企图尝试用 TypeScript 重构那些几十万行的老项目,也不可能将原来 Vue 或 JQuery 开发的项目用 React 重写一遍。所以我的目的是尽可能在未来开发新的项目时,可以使用这套模板。
首先我们要问自己一个问题。
一套开发模板的好处有很多。市面上比较流行的有 create-react-app 和 vue-cli 等等。他们已经做得很好了,为什么还要自己搭建一套呢?
如果回答不了这个问题,这个模板也不需要继续做了。
首先分析一下,它们具有一个统一的问题,并且无法解决,集成的东西不够多。框架的开发原则是越简单越好,但是开发模板则不一样,它应该是即开即用,将所有所需要的东西尽可能多地全部附带。而这同样让公开的、作为框架开发标准的开发模板无法做到。因为它们不知道你的项目会用到什么,也不知道你的项目会在解决同一类问题时使用哪些工具和库。
比如,它们不知道你需不需要 ajax 交互,也不知道你会使用哪种方法去发送 ajax,比如原生的 XHR、fetch 或者 jQuery 的 ajax 或者 axios。
它们也不知道你需不需要更高级的样式编写方式,也不知道你会使用哪种方法去编写样式,比如原生的 css、或者预处理器 less 或者 sass,postcss,也可能是 css-in-js,styled-components。
更多的问题我也不举例了,比如状态管理、路由、类型检测、代码风格、数据 mock、组件库、测试等等。它们都不知道你需不需要解决这些问题,以及用何种方式来解决这些问题。
它们也不可能替你去选择,那样会限制住你的技术选型。
如果你使用那种开发模板,只会有两种情况。第一,你选择每次使用 cli 命令创建全新的模板,那么你每次都需要去修改默认的配置文件和安装 npm 包。第二、你选择使用老项目配置好的模板,那么你每次都需要 copy 老项目的代码,然后从中删除原来与公用开发无关的代码。这两种情况都不是最好的办法。
因为这都是在人工处理,而非自动化的,这涉及到前端工程化。我们应该有一个更好的解决方案。
除了第一个问题,公用的开发模板还存在其它问题。
比如,如果你对开发模板不够了解,你是不知道它的默认配置是如何编写的,甚至不能随便修改 webpack 的版本。
作为一个自己每天都在使用的开发模板,我们应该了解它的全部,当出现问题时,我们能够迅速定位问题及解决问题,这种能力是每一个工程师都应该具备的能力。
同样,无论在工作中还是学习中,效率永远都是第一位的。没有一个足够熟悉的开发模板,我们的开发过程就象是瞎子过河,出了问题只能翻阅官方文档。如果能找到还好,找不到的话就只能去 Google 上面搜索别人提供的答案,然后粗略读一下,粘过来运行看看。不行的话再去寻找另一个答案,如此往复循环。
处于这种模式下的程序员,工作效率是极其低下的,而且成长效率几乎为零,唯一的好处大概就是在积累所谓的经验。长此以往,对公司不利,对自已同样不利。我们要具有独立思考的能力和全局的统筹能力,这一点十分重要。
最终的结论就是:**我们的技术选型,只有我们自己知道,也只有我们自己可以搭建一个适合自己的、灵活的开发模板。**所以开发模板的目标就是解决公用的模板无法解决的问题。
实操
现在我们开始正式搭建开发模板。这里默认你已经安装好 node.js 和 yarn。
首先创建一个文件夹。
mkdir react-template
进入文件夹。
cd react-template
初始化项目,所有问题默认选择 yes。
npm init -y
安装基本依赖。
yarn add -D typescript react react-dom webpack webpack-cli @types/react @types/react-dom
这些都是最基本的依赖。react 和 react-dom 是 react 必备的库,当然 react-dom 可以替换成其它渲染 jxs 的库。typescript 是使用 typescript 的基本库。webpack 是必备的核心库,webpack-cli 让我们能够在命令行中使用 webpack。@types 开头的库是 typescript 的类型库,只是在开发时使用。
编写项目入口文件
在项目根目录下创建 src 文件夹。
mkdir src
在文件夹内创建 index.tsx 作为入口文件,里面内容也比较简单。
import React from "react"; import ReactDOM from "react-dom"; ReactDOM.render(<div>Hello,World!</div>, document.getElementById("app"));
在项目根目录下创建一个 public 文件夹。
mkdir public
这个文件夹下面存放一些公共的资源。
在 public 文件夹下创建 index.htm 作为模板,内容如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <div id="app"></div> </body> </html>
<%= %>
是 html-webpack-plugin 的特定模板语法。
编写配置文件
编写 ts 配置
初始化 tsconf.json 配置。
执行命令
tsc --init
会在项目根目录下生成 tsconf.json 配置文件。
现在只需要将里面的 jsx 规则修改一下。
{ // 其他代码 "jsx": "react" }
编写 webpack 配置
接下来编写基本的 webpack 配置文件。
在根目录下创建 build 文件夹。
mkdir build
进入文件夹。
cd build
这里通过不同的环境编写不同的配置,这样便于维护。
创建四个文件。
- webpack.config.js 真正使用的配置文件。
- webpack.base.config.js 基础配置文件。
- webpack.dev.config.js 开发环境配置文件。
- webpack.prod.config.js 生产环境配置文件。
webpack.base.config.js 作为基础配置文件,再通过检测运行环境,来判断该使用哪一个配置文件和基础配置文件合并。
最后通过 webpack.config.js 来作为统一的配置。
先安装 3 个包。
yarn add -D html-webpack-plugin webpack-dev-server ts-loader
html-webpack-plugin 可以生成 html,并自动导入打包后的 js 文件。
webpack-dev-server 提供一个本地开发服务。
ts-loader 用于编译 ts 和 tsx 文件。
编写 webpack.base.config.js
// webpack.base.config.js var path = require("path"); var HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.tsx", output: { path: path.resolve(__dirname, "../dist"), filename: "index.js", }, module: { rules: [ { test: /\.(ts|tsx)/, loader: "ts-loader", }, ], }, plugins: [ new HtmlWebpackPlugin({ title: "react 开发模板", template: "./public/index.htm", }), ], };
编写 webpack.dev.config.js
// webpack.dev.config.js var path = require("path"); module.exports = { mode: "development", devServer: { contentBase: path.resolve(__dirname, "../dist"), host: "127.0.0.1", port: 3000, hot: true, open: true, historyApiFallback: { index: "/index.html", }, }, };
编写 webpack.prod.config.js
// webpack.prod.config.js module.exports = { mode: "production", };
这里要用到一个合并配置的插件。
yarn add -D webpack-merge
编写 webpack.config.js 文件
// webpack.config.js var baseConfig = require("./webpack.base.config"); var devConfig = require("./webpack.dev.config"); var prodConfig = require("./webpack.prod.config"); var merge = require("webpack-merge"); module.exports = (env, argv) => { var envConfig = argv.mode === "development" ? devConfig : prodConfig; var config = merge(baseConfig, envConfig); return config; };
全部编写完成后,在 package.json 文件中添加开发和编译的脚本。
{ "scripts": { // 其他代码 "start": "webpack-dev-server --config ./build/webpack.config.js --mode development", "build": "webpack --config ./build/webpack.config.js --mode production" }
测试
执行yarn start
命令,会自动在默认的浏览器中打开一个页面,显示出 Hello,World!字样。
接着再测试构建命令,执行yarn build
命令。会在项目根目录下生成 dist 目录,里面包含一个 index.html 和一个 index.js 文件。
这样一个最基本的基于 React 和 TypeScript 的开发模板已经搭建完毕。接下来我会在此模板的基础上进行持续优化和迭代。