最近写了一个基于React的UI库项目,本意是希望使用TypeScript去写。正好Webpack4.x也没有配过,正好能看一下比起webpack3的配置有哪些区别。这次的教程就主要写下创建项目的顺序,还有尿点。
首先创建一个文件夹,然后使用yarn创建项目和package.json文件
接下去就是各种回车和输入,知道package.json文件创建完成。
接着创建目录
-build
|-webpack.base.js
|-webpack.dev.js
|-webpack.prd.js
-src
|-component
|-style
|-main.tsx
-index.html
-index.js
index.html: 模板文件
index.js: npm插件通过import引入的入口文件,配合package.json里的main字段使用,注意路径
src-component: react的组件目录
src-style: 存放scss样式文件
main.tsx: typescript的入口文件
然后我们需要webpack进行打包
1
| yarn add -D webpack webpack-dev-middleware webpack-hot-middleware@next
|
配置webpack.base.js这个开发和生产环境都需要的各种loader和通用配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| module.exports = { // 开发端口 devPort: 3000, // 关联文件类型 resolve: { extensions: ['.ts', '.tsx', '.js', '.scss', '.css', '.png', '.gif', '.jpg'] }, // loader rules: [{ test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file-loader?name=fonts/[name].[ext]' }, { test: /\.(png|jpg|gif)$/, loader: process.env.NODE_ENV=='prd' ? ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader!postcss-loader!sass-loader" }) : 'url-loader?limit=8192&name=images/[name].[ext]' }, { test: /\.css$/, loader: "style-loader!css-loader" }, { test: /\.(scss|sass)$/, loader: "style-loader!css-loader!postcss-loader!sass-loader" }, { test: /\.js?$/, //表示要变异的文件的类型,这里要编译的是js文件 loader: 'babel-loader', //装载的哪些模块 exclude: /node_modules/, //标示不变异node_modules文件夹下面的内容 query: { //具体的编译的类型, compact: false, //表示不压缩 } }, { test: /\.tsx$/, enforce: 'pre', loader: ['awesome-typescript-loader', 'tslint-loader'] } ] }
|
安装各种loader到依赖里,还有babel,autoprefixer和typescript的支持
1
| yarn add -D autoprefixer awesome-typescript-loader babel-core babel-preset-react babel-loader babel-polyfill babel-preset-env babel-preset-typescript css-loader file-loader node-sass postcss-loader sass-loader style-loader ts-loader typescript url-loader
|
安装完成后,需要在根目录下配置TypeScript配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // tsconfig.json { "compilerOptions": { "sourceMap": true, "allowJs": true, "module": "commonjs", "removeComments": true, // 可以使用 Promise, async, await等新语法 "target": "es2017", "jsx": "react" }, "exclude": [ "node_modules" // 这个目录下的代码不会被 typescript 处理 ], "include": [ "./src/**/*" ] }
|
autoprefixer的配置文件,需要依赖postcss-loader,已安装,在根目录下创建
1 2 3 4 5 6
| // postcss.config.js module.exports = { plugins: [ require('autoprefixer')({ browsers: ['iOS>7', 'Android>4'] }) ] };
|
我们还需要使用tslint规则来检验代码的统一性和可读性
1
| yarn add -D tslint tslint-loader tslint-react
|
然后再根目录下创建tslint.json文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| { "extends": [ "tslint:latest", "tslint-react" ], "rules": { // 是否允许使用字符串直接访问object的属性 "no-string-literal": false, // 检查分号,忽略interfaces "semicolon": [ true, "always", "ignore-interfaces" ], // 引号,双单使用,一般使用单引号,jsx里使用双引号 "quotemark": [ true, "single", "jsx-double", "avoid-template", "avoid-escape" ], // 4个空格代替tab键 "indent": [ true, "spaces", 4 ], // 禁止出现重复的import "no-duplicate-imports": true, // 禁止一个文件中出现多个相同的 namespace "no-mergeable-namespace": true, // 文件类型必须时 utf-8 "encoding": true, // import 语句中,关键字之间的间距必须是一个空格 "import-spacing": true, // 接口可以 implement extend 和 merge "interface-over-type-literal": true, // new后面要有空格 "new-parens": true, // 禁止debugger "no-debugger": false, // 禁止行尾有空格 "no-trailing-whitespace": false, // 禁止无用的表达式 "no-unused-expression": true, // 禁止未使用的变量 "no-unused-variable": true, // 不能未申明就使用 "no-use-before-declare": true, // 不能使用var "no-var-keyword": true, // interface的命名不用必须带大写“I” "interface-name": [ true, "never-prefix" ], "no-console": ["error", { "allow": ["warn", "error"] }] } }
|
babel语法的支持也需要在根目录下创建.babelrc文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| // .babelrc { "presets": [ [ "env", { "targets": { "browsers": [ "last 2 versions", "safari >= 7" ] } } ], [ "react" ], [ "typescript" ] ], "plugins": [ "react-hot-loader/babel" ] }
|
配置webpack的开发配置文件
要安装依赖先,还需要支持热更新
1
| yarn add -D html-webpack-plugin react-hot-loader
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| // build/webpack.dev.js var path = require('path'); var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var autoprefixer = require('autoprefixer'); var global = require('./webpack.base.js'); module.exports = { // Enable sourcemaps for debugging webpack's output. devtool: "source-map", entry: { app: [ // `webpack-dev-server/client?http://localhost:8080/`, 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=10000&reload=true', // 热更新 'babel-polyfill', 'webpack-hot-middleware/client', path.join(__dirname, '../src/main.tsx') ] }, output: { //输出 path: path.join(__dirname, '../dist'), filename: "bundle.js", publicPath: '/' }, mode: "development", // webpack4.x后需要加入的新选项,必须写 performance: { hints: false }, module: { rules: global.rules }, resolve: global.resolve, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', inject: 'body', template: path.join(__dirname, '../index.html') //模板地址 }), // OccurenceOrderPlugin is needed for webpack 1.x only new webpack.optimize.OccurrenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), // Use NoErrorsPlugin for webpack 1.x new webpack.NoEmitOnErrorsPlugin() ], cache: true }
|
配置webpack.prd.js文件,因为要把css分离出来单独从外部引入,所以需要安装插件
1
| yarn add -D extract-text-webpack-plugin
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| var path = require('path'); var webpack = require('webpack'); var autoprefixer = require('autoprefixer'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { devtool: 'cheap-module-eval-source-map', entry: { vendor: ['react', 'react-dom'], app: [ 'babel-polyfill', path.join(__dirname, '../src/main.tsx') ] }, //入口 output: { //输出 path: path.join(__dirname, '../public'), filename: "cui.min.js", publicPath: './' }, mode: "production", module: { rules: global.rules }, resolve: global.resolve, plugins: [ new webpack.LoaderOptionsPlugin({ options: { postcss: [ autoprefixer(), ] } }), new ExtractTextPlugin({ filename: './cui.min.css' }), ], // 分片及vendor提取 optimization: { splitChunks: { cacheGroups: { commons: { chunks: 'initial', minChunks: 2, maxInitialRequests: 5, minSize: 0 }, vendor: { test: /node_modules/, chunks: 'initial', name: 'vendor', priority: 10, enforce: true } } } }, cache: true }
|
然后编辑入口文件,因为使用了TypeScript来编写文件,react之前并未使用TypeScript编写,所以不支持,那我们在安装依赖时需要多安装type包
1
| yarn add react react-dom @types/react @types/react-dom
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| // src/app.tsx import * as React from 'react'; import Input from './component/Input'; class App extends React.Component<{}, {}> { public render() { return ( <Input text="Hello world!!" /> ); } } export default App; // src/component/Input.tsx import * as React from 'react'; interface HelloText { text: string; } interface ReactState { name: any; } class Input extends React.Component<HelloText, ReactState> { constructor(props) { super(props); this.state = { name: 'King', }; this.onClickHandler = this.onClickHandler.bind(this); } public onClickHandler(): void { this.setState({ name: 'Tina', }); } public render() { return ( <div> <h1>First Demo</h1> <div>Props: {this.props['text']}</div> <div>state: {this.state['name']}</div> <button onClick={this.onClickHandler} > click me! </button> </div> ); } } export default Input; // src/main.tsx import * as React from 'react'; import * as ReactDom from 'react-dom'; import App from './app'; import './style/index.scss'; ReactDom.render(<App />, document.getElementById('app')); if (module.hot) { module.hot.accept(); }
|
鉴于TypeScript的严格模式,热更新需要在main文件中监听webpack传入的module,但是这个变量在目前环境中拿不到,所以我们还需要一个支持types的webpack插件
1
| yarn add -D @types/webpack-env
|
1 2 3 4 5 6 7
| // 在if(module.hot)前对module定义下 declare var module: __WebpackModuleApi.Module; if (module.hot) { module.hot.accept(); }
|
总结
文档先写到这,如果有不清晰的,我会再补充,详细的项目地址可以看这里