Open wython opened 5 years ago
这是一篇关于搭建react项目的基础文章。我决定一个react项目包含前端工程化部分和react技术栈部分。对于前端工程化,采用webpack做工程化是主流的方式。react的技术栈会用到redux和react-router。
webpack的功能是模块化打包工具。
所以,直接使用babel是可以编译react的jsx的,但是使用webpack做工程化很重要,很多开发工作都需要用到webpack,所以可以先了解基础的webpack功能。先创建文件夹 an-react-project
npm init
npm install --save-dev webpack webpack-cli webpack-dev-server
初始化npm, 安装webpack。然后弄一个简单的目录结构了解下webpack的基本功能。这样安装默认就是最新webpack4
|-- dist |-- index.html |-- src |-- index.js |-- moduleA.js |-- moduleB.js |-- package.json |-- webpack.config.js
webpack.config.js:
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } }
src/index.js:
// index.js import moduleA from './moduleA'; import moduleB from './moduleB'; function createComponent() { var element = document.createElement('div'); var btn = document.createElement('button'); var btnTwo = document.createElement('button'); element.innerHTML = 'Hello World'; btn.innerHTML = 'print btn'; element.appendChild(btn); element.appendChild(btnTwo); btn.onclick = moduleA btnTwo.onclick = moduleB return element; } document.body.appendChild(createComponent())
src/moduleA.js:
export default function printHello() { console.log('Ok') console.log('From printHello') }
src/moduleB.js:
export default function printHelloTwo() { console.log('Yes') console.log('From moduleB') }
package.js定义命令:
... "scripts": { "start": "webpack --config webpack.config.js", }, ...
执行npm run start 或者 yarn start命令可以看到dist中已经打包出了bundle.js。直接访问index.html可以看到静态页面。目前的配置和react没有任何关系,仅仅只是webpack的基本功能。并且定义了一个入口和输出路径。具体的可以看webpack文档指南。有更详细的配置细节。
source map: webpack打包后的代码,如果需要追踪错误位置。就比较难,source map功能可以定义webpack配置的devtool来追踪源代码。
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, devtool: 'inline-source-map' }
不过越原始的追踪带来性能会更差。可以看下文档支持的配置: webpack devtool
webpack-dev-server:
上面已经安装了webpack-dev-server。开发环境通过devServer配置开启。可以不需要每次修改都重新编译。实时监听编译。webpack有三种方式支持监听,watch配置,webpack-dev-middleware配置。webpack-dev-server就是基于webpack-dev-middleware实现的,同时具有更多功能配置。一般都会用webpack-dev-server,但是如果希望自己编写server逻辑,可以考虑结合node后端和middleware自己实现。
const path = require('path'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, devtool: 'inline-source-map', devServer: { contentBase: './dist' } }
这个时候修改package.json启动命令用webpack-dev-server启动可以看到浏览器会用node服务方式访问页面。
webpack插件配置html-webpack-plugin和clean-webpack-plugin:
当然,因为index.html是我们自己编写的,一般会通过html-webpack-plugin维护html,这个插件非常有必要,因为对于后面的项目部署,动态生成hash文件名的方式引入html中,如果人为维护基本是一件很繁琐的事情,插件可以根据配置自己引入script脚本。
clean-webpack-plugin用于每次启动或者编译工程时候保持文件夹是干净的。它会清理文件夹下的命令。
webpack插件的配置通过plugin数组配置。
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, devtool: 'inline-source-map', devServer: { contentBase: './dist' }, plugins: [ new CleanWebpackPlugin({ default: [ 'dist' ] }), new HtmlWebpackPlugin({ title: 'hello world' }) ] }
再次启动项目时候,这时候dist文件夹应该已经没有文件。因为开发环境下,dev-server是将编译文件载入内存中。这样可以提高更新效率,因为对计算机而已,读取硬盘比读内存要耗时的多。
Babel的工作是转换js语法,比如平时用到的jsx,浏览器不支持的es6语法,ts语法。都是babel做编译的工作。如果不了解每一个模块的职责,很容易混淆webpack,babel的关系。
配置babel有两种方式,一种是通过创建babel.config.js配置文件,另一种是.babelrc。前者是js形式,如果希望做一些脚本工作通过配置去配置是不错的。不过我们需要借助webpack loader方式去做,所以不需要在两个文件中做配置。
babel是通过plugins和presets的两种方式去扩展需要的语法。
{ "presets": [], "plugins": [] }
presets是一组plugins的集合。一般来说用已有的preset足够满足要求。安装babel和@babel/core。和react的@babel/preset-react。同时安装react和react-dom框架和
yarn add babel @babel/core @babel/preset-react --dev yarn add react react-dom --save
重新编辑src/index.js:
import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render(<h1>hello world!</h1>, document.getElementById('app'))
现在只有一个webpack.config.js,不过一般项目会分开发环境和生产环境,不同的环境webpack的职责也不同。所以可以提前建好不同的配置文件,通过webpack-merge合并配置。
yarn add webpack-merge --dev
我自己的话,创建文件夹build,把配置文件放进去。文件目录如下:
|-- package.json |-- yarn.lock |-- build | |-- webpack.base.config.js | |-- webpack.dev.config.js | |-- webpack.pro.config.js | |-- webpack.vendor.config.js |-- dist |-- src |-- index.js |-- asset |-- index.html
webpack.base.config.js:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CleanWebpackPlugin({ default: [ 'dist' ] }), new HtmlWebpackPlugin({ template: './src/asset/index.html' }) ] }
webpack.dev.config.js:
const webpackMerge = require('webpack-merge'); const baseConfig = require('../build/webpack.base.config'); const path = require('path'); const srcPath = path.join(__dirname, '../src'); const devConfig = { mode: 'development', devtool: 'inline-source-map', devServer: { contentBase: './dist' }, module: { rules: [ { test: /\.(js|jsx)$/, include: srcPath, use: [ { loader: 'babel-loader', options: { presets: ['@babel/preset-react'], } } ] } ] } } module.exports = webpackMerge(baseConfig, devConfig);
修改package.json命令:
... "scripts": { "start": "webpack-dev-server --open --config ./build/webpack.dev.config.js" } ...
此时,通过访问server对应页面可以看到结果,说明jsx代码已经成功转义。基础配置完成。
以上配置相对基础,优化是一个持续过程,但是如果一开始能做好的优化,对后续会更有帮助。对webpack的优化可以分开发环境下的优化和生产环境下的优化
开发环境下,需要提高实时编译时间,做到最好的开发体验。
先介绍下dllPlugin,这个组件用用于单独抽离部分公共组件库。平时开发过程中,有些库,例如上面涉及到的react,react-dom这些库,一般一个项目定型之后,不会频繁修改库的内容和版本。所以上面的配置每一次启动项目都会编译一次公共库。实际上是没有必要的,因为这个过程是重复的,公共库并没有发生变化。最好的思路是将他们提取出来,之后每一次构建就不会再去编译这些代码。
要使用dllPlugin,只需要在webpack.vendor.config.js中配置插件和需要打包的包。然后通过DllReferencePlugin引用依赖关系即可。
在webpack.vendor.config.js中:
const webpack = require('webpack'); const path = require('path'); module.exports = { mode: 'development', entry: { vendor: ['react', 'react-dom'] }, output: { filename: '[name].dll.js', path: path.join(__dirname, '..', 'dist'), library: 'vendor_lib_[hash]' }, plugins: [ new webpack.DllPlugin({ context: __dirname, // 上下文 path: path.join(__dirname, '..', 'dist', 'vendor-manifest.json'), name: 'vendor_lib_[hash]' // 与out的libirary库名保持一致 }) ] }
插件的path定义的是依赖文件的保存路径,webpack的另一个插件需要这个依赖文件来保证能访问对应库。
const webpackMerge = require('webpack-merge'); const baseConfig = require('../build/webpack.base.config'); const webpack = require('webpack'); const path = require('path'); const srcPath = path.join(__dirname, '../src'); const devConfig = { mode: 'development', devtool: 'inline-source-map', devServer: { contentBase: './dist' }, plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('../dist/vendor-manifest.json') }) ], module: { rules: [ { test: /\.(js|jsx)$/, include: srcPath, use: [ { loader: 'babel-loader', options: { presets: ['@babel/preset-react'], } } ] } ] } } module.exports = webpackMerge(baseConfig, devConfig);
然后通过DllReferencePlugin定义即可,具体参数可以看官方文档的配置项目。
热替换功能用于提高开发效率,它的功能是可以无刷新页面的情况下重新载入新模块。这个不能和dev-server的实时监控搞混。现在虽然代码修改,页面实时刷新,但是热替换可以做到不刷新页面就能显示内容的更改。
想象这样一个场景,平时在开发类似modal弹窗这样的组件时候,如果你修改了modal页面刷新,modal就不见了,需要重新弹窗。如果用了热替换,实时修改将不会刷新页面,弹窗不会消失,这对开发效率是有一定提高的。
在src文件夹下加上两个文件:
|-- src |-- index.js + |-- app.js + |-- header.js
app.js:
import React, { Component, Fragment } from 'react'; import Header from './header'; export default class App extends Component { render() { return ( <Fragment> <Header /> <h1>hello world!</h1> </Fragment> ) } }
header.js:
import React, { Component } from 'react'; export default class Header extends Component { render() { return ( <header>头部</header> ) } }
index.js:
import React from 'react'; import ReactDOM from 'react-dom'; import App from './app'; ReactDOM.render(<App />, document.getElementById('app'))
现在如果修改header.js组件页面会刷新一下。
要开启webpack热替换,需要以下步骤:
即:
... devServer: { contentBase: './dist', hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ... ], ...
if (module.hot) { module.hot.accept('./app.js', function(){ // app.js变化之后需要做的操作,这根据具体场景配置 }) }
对于react项目,需要用到第三方的插件 react-hot-loader。基于webpack的HotModuleReplacementPlugin,所以上面webpack的配置应该保留。但是入口文件则无需你处理module.hot.accept里面的逻辑。
安装:
yarn add react-hot-loader --dev
入口组件,比如上面react例子中的app.js
import React, { Component, Fragment } from 'react'; import Header from './header'; import { hot } from 'react-hot-loader/root'; const App = class App extends Component { render() { return ( <Fragment> <Header /> <h1>hello world!</h1> </Fragment> ) } } export default hot(App);
我是写在options里面,如果使用.babelrc配置,一样道理。
{ presets: ['@babel/preset-react'], plugins: ['react-hot-loader/babel'] }
此时热更新应该生效,修改header文本,可以看到页面无刷新修改了最新内容。不过此时启动项目,react-hot-loader会有个wraning,大体意思是对于react 16.4之后的版本,应该使用扩展@hot-loader/react-dom,否则无法在部分特性里面使用热更新,其实指的是react hook的语法。
只需要按照扩展,然后定义别名使得原始的react-dom包从@hot-loader/react-dom中获取就行了。
yarn add @hot-loader/react-dom
webpack.dev.config.js完整配置如下:
const webpackMerge = require('webpack-merge'); const baseConfig = require('../build/webpack.base.config'); const webpack = require('webpack'); const path = require('path'); const srcPath = path.join(__dirname, '../src'); const devConfig = { mode: 'development', devtool: 'inline-source-map', plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('../dist/vendor-manifest.json') }), new webpack.HotModuleReplacementPlugin() ], devServer: { contentBase: './dist', hot: true }, resolve: { alias: { 'react-dom': '@hot-loader/react-dom' } }, module: { rules: [ { test: /\.(js|jsx)$/, include: srcPath, use: [ { loader: 'babel-loader', options: { presets: ['@babel/preset-react'], plugins: ['react-hot-loader/babel'] } } ] } ] } } module.exports = webpackMerge(baseConfig, devConfig);
其实我上面配置对插件的include已经设置了,实际上就是js文件只查找src文件夹下的文件。
这个是具有灵活性的优化,比如以上的babel-loader。来看看官方文档对babel-loader的自我评价。
babel-loader可以通过cacheDirectory实现缓存。所以,用上它。修改配置:
... rules: [ { test: /\.(js|jsx)$/, include: srcPath, use: [ { loader: 'babel-loader', options: { presets: ['@babel/preset-react'], plugins: ['react-hot-loader/babel'], cacheDirectory: '../runtime_cache/' } } ] } ] ...
作用:webpack本身是单线程处理模块的,happyPack可以让部分loader在多线程下去处理文件。
那平时对一些比较耗时的loader可以使用happyPack做性能优化。比如上面的babel,它自己都说自己很慢。
使用happypack,需要修改loader和插件,我们只修改部分费时的loader就行了,loader里面:
webpack.dev.config.js
const webpackMerge = require('webpack-merge'); const baseConfig = require('../build/webpack.base.config'); const webpack = require('webpack'); const HappyPack = require('happypack'); const path = require('path'); const srcPath = path.join(__dirname, '../src'); const devConfig = { mode: 'development', devtool: 'inline-source-map', plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('../dist/vendor-manifest.json') }), new webpack.HotModuleReplacementPlugin(), new HappyPack({ id: 'js', loaders: [ { loader: 'babel-loader', options: { presets: ['@babel/preset-react'], plugins: ['react-hot-loader/babel'], cacheDirectory: '../runtime_cache/' } } ] }) ], devServer: { contentBase: './dist', hot: true }, resolve: { alias: { 'react-dom': '@hot-loader/react-dom' } }, module: { rules: [ { test: /\.(js|jsx)$/, include: srcPath, use: 'happypack/loader?id=js' } ] } } module.exports = webpackMerge(baseConfig, devConfig);
对于一个开发团队来说,ts可能不是必要的。首先,要权衡typescript是否适合在一个项目中使用。ts是js的一个超集。它提供了一些类型校验的东西,当然也会牺牲一些开发效率上的东西。但是从长远来说,对开发效率更多是利大于弊,它可以避免一些人为错误,数据流转中松散类型带来对不确定问题。我觉得一个项目适不适合用ts,主要还是看这个项目是不是长期迭代,体量的大小。对于一些用完即走的活动页,可能ts反而是一个累赘。另外,本身折腾ts也是一个繁琐的事情,包括接口,类型的定义,有时候比正常js会多一些工作量。如果不按ts规范走,那其写法也和js没有区别,还不如不用。
webpack中使用ts思路很简单。因为本身ts只需要一个tsconfig.json的文件。而前端打包ts主要还是ts-loader和babel-loader两种。这里我两种都尝试了,最后babel-loader对热加载更友好。因为热加载插件中官方文档对ts对说法是这样的。
大体意思是,对于react-hot-loader 4的版本来说,你必须用babel转换才行,这对于一些其实不需要使用babel的用户来说不太友好。幸运的是,babel的配置很简单,而且集成的也很好。所以,让你大胆的用babel-loader代替ts-loader去做这件事。
安装: @babel/preset-env, @babel/preset-typescript,@babel/plugin-proposal-decorators,@babel/plugin-proposal-class-properties 四个preset集合,env集成了很多常用写法,typescript就是用babel转ts的集合。后面两个是在ts中使用装饰器语法所用。 安装: core-js regenerator-runtime babel7.4之后把polyfill拆分成两个模块。如果需要做babel升级迁移,要考虑polyfill问题。
base.js:
extensions: ['.ts', '.tsx', '.js', '.json'], // 默认是['.js', '.json'], ts需要扩展支持的后缀名
配置功能是不需要写后缀即可导入模块
test正则修改为:test: /.(j|t)sx?$/, loader中添加babel上面装对preset,和插件
new HappyPack({ id: 'js', loaders: [ { loader: 'babel-loader', type: 'javascript/auto', options: { presets: [ +++ "@babel/preset-env", +++ "@babel/preset-typescript", '@babel/preset-react' ], plugins: [ +++ ['@babel/plugin-proposal-decorators', { legacy: true }], +++ ['@babel/plugin-proposal-class-properties', { loose: true }], 'react-hot-loader/babel' ], cacheDirectory: './runtime_cache/' } } ] })
然后src都.js文件都改成ts写法。基本ts的引入就完成了。
less需要less-loader转换,css需要css-loader转换,style-loader将样式提取到style标签中,生产环境则用mini-css-extract-plugin将样式提取到单独文件中。
less: less-loader() css-loader(解释(interpret) @import 和 url()) extract-loader to-string-loader style-loader(inject
一个react项目
这是一篇关于搭建react项目的基础文章。我决定一个react项目包含前端工程化部分和react技术栈部分。对于前端工程化,采用webpack做工程化是主流的方式。react的技术栈会用到redux和react-router。
webpack的职责
webpack的功能是模块化打包工具。
使用webpack 4
所以,直接使用babel是可以编译react的jsx的,但是使用webpack做工程化很重要,很多开发工作都需要用到webpack,所以可以先了解基础的webpack功能。先创建文件夹 an-react-project
初始化npm, 安装webpack。然后弄一个简单的目录结构了解下webpack的基本功能。这样安装默认就是最新webpack4
webpack.config.js:
src/index.js:
src/moduleA.js:
src/moduleB.js:
package.js定义命令:
执行npm run start 或者 yarn start命令可以看到dist中已经打包出了bundle.js。直接访问index.html可以看到静态页面。目前的配置和react没有任何关系,仅仅只是webpack的基本功能。并且定义了一个入口和输出路径。具体的可以看webpack文档指南。有更详细的配置细节。
source map: webpack打包后的代码,如果需要追踪错误位置。就比较难,source map功能可以定义webpack配置的devtool来追踪源代码。
不过越原始的追踪带来性能会更差。可以看下文档支持的配置: webpack devtool
webpack-dev-server:
上面已经安装了webpack-dev-server。开发环境通过devServer配置开启。可以不需要每次修改都重新编译。实时监听编译。webpack有三种方式支持监听,watch配置,webpack-dev-middleware配置。webpack-dev-server就是基于webpack-dev-middleware实现的,同时具有更多功能配置。一般都会用webpack-dev-server,但是如果希望自己编写server逻辑,可以考虑结合node后端和middleware自己实现。
这个时候修改package.json启动命令用webpack-dev-server启动可以看到浏览器会用node服务方式访问页面。
webpack插件配置html-webpack-plugin和clean-webpack-plugin:
当然,因为index.html是我们自己编写的,一般会通过html-webpack-plugin维护html,这个插件非常有必要,因为对于后面的项目部署,动态生成hash文件名的方式引入html中,如果人为维护基本是一件很繁琐的事情,插件可以根据配置自己引入script脚本。
clean-webpack-plugin用于每次启动或者编译工程时候保持文件夹是干净的。它会清理文件夹下的命令。
webpack插件的配置通过plugin数组配置。
再次启动项目时候,这时候dist文件夹应该已经没有文件。因为开发环境下,dev-server是将编译文件载入内存中。这样可以提高更新效率,因为对计算机而已,读取硬盘比读内存要耗时的多。
Babel的职责
Babel的工作是转换js语法,比如平时用到的jsx,浏览器不支持的es6语法,ts语法。都是babel做编译的工作。如果不了解每一个模块的职责,很容易混淆webpack,babel的关系。
使用babel
配置babel有两种方式,一种是通过创建babel.config.js配置文件,另一种是.babelrc。前者是js形式,如果希望做一些脚本工作通过配置去配置是不错的。不过我们需要借助webpack loader方式去做,所以不需要在两个文件中做配置。
babel是通过plugins和presets的两种方式去扩展需要的语法。
presets是一组plugins的集合。一般来说用已有的preset足够满足要求。安装babel和@babel/core。和react的@babel/preset-react。同时安装react和react-dom框架和
重新编辑src/index.js:
整理webpack配置文件
现在只有一个webpack.config.js,不过一般项目会分开发环境和生产环境,不同的环境webpack的职责也不同。所以可以提前建好不同的配置文件,通过webpack-merge合并配置。
我自己的话,创建文件夹build,把配置文件放进去。文件目录如下:
webpack.base.config.js:
webpack.dev.config.js:
修改package.json命令:
此时,通过访问server对应页面可以看到结果,说明jsx代码已经成功转义。基础配置完成。
webpack的一些优化
以上配置相对基础,优化是一个持续过程,但是如果一开始能做好的优化,对后续会更有帮助。对webpack的优化可以分开发环境下的优化和生产环境下的优化
开发环境
开发环境下,需要提高实时编译时间,做到最好的开发体验。
1. dllPlugin提取公共库
先介绍下dllPlugin,这个组件用用于单独抽离部分公共组件库。平时开发过程中,有些库,例如上面涉及到的react,react-dom这些库,一般一个项目定型之后,不会频繁修改库的内容和版本。所以上面的配置每一次启动项目都会编译一次公共库。实际上是没有必要的,因为这个过程是重复的,公共库并没有发生变化。最好的思路是将他们提取出来,之后每一次构建就不会再去编译这些代码。
要使用dllPlugin,只需要在webpack.vendor.config.js中配置插件和需要打包的包。然后通过DllReferencePlugin引用依赖关系即可。
在webpack.vendor.config.js中:
插件的path定义的是依赖文件的保存路径,webpack的另一个插件需要这个依赖文件来保证能访问对应库。
webpack.dev.config.js:
然后通过DllReferencePlugin定义即可,具体参数可以看官方文档的配置项目。
2. 使用热替换(HRM)
热替换功能用于提高开发效率,它的功能是可以无刷新页面的情况下重新载入新模块。这个不能和dev-server的实时监控搞混。现在虽然代码修改,页面实时刷新,但是热替换可以做到不刷新页面就能显示内容的更改。
想象这样一个场景,平时在开发类似modal弹窗这样的组件时候,如果你修改了modal页面刷新,modal就不见了,需要重新弹窗。如果用了热替换,实时修改将不会刷新页面,弹窗不会消失,这对开发效率是有一定提高的。
在src文件夹下加上两个文件:
app.js:
header.js:
index.js:
现在如果修改header.js组件页面会刷新一下。
要开启webpack热替换,需要以下步骤:
即:
react项目的热更新配置
对于react项目,需要用到第三方的插件 react-hot-loader。基于webpack的HotModuleReplacementPlugin,所以上面webpack的配置应该保留。但是入口文件则无需你处理module.hot.accept里面的逻辑。
安装:
入口组件,比如上面react例子中的app.js
app.js:
我是写在options里面,如果使用.babelrc配置,一样道理。
此时热更新应该生效,修改header文本,可以看到页面无刷新修改了最新内容。不过此时启动项目,react-hot-loader会有个wraning,大体意思是对于react 16.4之后的版本,应该使用扩展@hot-loader/react-dom,否则无法在部分特性里面使用热更新,其实指的是react hook的语法。
只需要按照扩展,然后定义别名使得原始的react-dom包从@hot-loader/react-dom中获取就行了。
webpack.dev.config.js完整配置如下:
减少查找范围
其实我上面配置对插件的include已经设置了,实际上就是js文件只查找src文件夹下的文件。
针对性的loader实行优化
这个是具有灵活性的优化,比如以上的babel-loader。来看看官方文档对babel-loader的自我评价。
babel-loader可以通过cacheDirectory实现缓存。所以,用上它。修改配置:
有名气的happypack
作用:webpack本身是单线程处理模块的,happyPack可以让部分loader在多线程下去处理文件。
那平时对一些比较耗时的loader可以使用happyPack做性能优化。比如上面的babel,它自己都说自己很慢。
使用happypack,需要修改loader和插件,我们只修改部分费时的loader就行了,loader里面:
webpack.dev.config.js
整理目录接入ts
对于一个开发团队来说,ts可能不是必要的。首先,要权衡typescript是否适合在一个项目中使用。ts是js的一个超集。它提供了一些类型校验的东西,当然也会牺牲一些开发效率上的东西。但是从长远来说,对开发效率更多是利大于弊,它可以避免一些人为错误,数据流转中松散类型带来对不确定问题。我觉得一个项目适不适合用ts,主要还是看这个项目是不是长期迭代,体量的大小。对于一些用完即走的活动页,可能ts反而是一个累赘。另外,本身折腾ts也是一个繁琐的事情,包括接口,类型的定义,有时候比正常js会多一些工作量。如果不按ts规范走,那其写法也和js没有区别,还不如不用。
webpack中使用ts思路很简单。因为本身ts只需要一个tsconfig.json的文件。而前端打包ts主要还是ts-loader和babel-loader两种。这里我两种都尝试了,最后babel-loader对热加载更友好。因为热加载插件中官方文档对ts对说法是这样的。
大体意思是,对于react-hot-loader 4的版本来说,你必须用babel转换才行,这对于一些其实不需要使用babel的用户来说不太友好。幸运的是,babel的配置很简单,而且集成的也很好。所以,让你大胆的用babel-loader代替ts-loader去做这件事。
安装: @babel/preset-env, @babel/preset-typescript,@babel/plugin-proposal-decorators,@babel/plugin-proposal-class-properties 四个preset集合,env集成了很多常用写法,typescript就是用babel转ts的集合。后面两个是在ts中使用装饰器语法所用。 安装: core-js regenerator-runtime babel7.4之后把polyfill拆分成两个模块。如果需要做babel升级迁移,要考虑polyfill问题。
base.js:
配置功能是不需要写后缀即可导入模块
test正则修改为:test: /.(j|t)sx?$/, loader中添加babel上面装对preset,和插件
然后src都.js文件都改成ts写法。基本ts的引入就完成了。
引入less和文件模块化
less需要less-loader转换,css需要css-loader转换,style-loader将样式提取到style标签中,生产环境则用mini-css-extract-plugin将样式提取到单独文件中。
less: less-loader() css-loader(解释(interpret) @import 和 url()) extract-loader to-string-loader style-loader(inject