最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

如何基于webpack4搭建一个react脚手架的过程分析

来源:动视网 责编:小采 时间:2020-11-27 19:33:09
文档

如何基于webpack4搭建一个react脚手架的过程分析

如何基于webpack4搭建一个react脚手架的过程分析:本篇文章分享给大家的内容是关于如何基于webpack4搭建一个react脚手架的过程分析,内容很详细,接下来我们就来看看具体的内容,希望可以帮助到有需要的朋友。react-sample-javascriptReact 16.0 boilerplate with react-router-do
推荐度:
导读如何基于webpack4搭建一个react脚手架的过程分析:本篇文章分享给大家的内容是关于如何基于webpack4搭建一个react脚手架的过程分析,内容很详细,接下来我们就来看看具体的内容,希望可以帮助到有需要的朋友。react-sample-javascriptReact 16.0 boilerplate with react-router-do


本篇文章分享给大家的内容是关于如何基于webpack4搭建一个react脚手架的过程分析,内容很详细,接下来我们就来看看具体的内容,希望可以帮助到有需要的朋友。

react-sample-javascript

React 16.0 boilerplate with react-router-dom, redux & webpack 4. (for javascript)
github项目地址

项目初始化

统一规范代码格式

  1. 配置 .editorconfig 使得IDE的方式统一 (见代码)

  2. 配置 .eslintrc.js 使得代码规范统一 (见代码)

    预期功能

  3. 管理资源: 能加载css、sccc、less、以及静态文件

  4. 管理输出:将打包后的静态文件输出至static目录下,以各自的文件类型管理

  5. dev:使用source map,方便调试时代码定位

  6. dev:配置devServer,并配置热替换,热加载,自动刷新,自动打开浏览器,并预留proxyTable

  7. dev:设置默认打开8080,被占用则寻找下一个空接口

  8. production:代码分离,打包css文件,css代码压缩,js代码压缩,输出到模板html,配置gzip

  9. analysis::使用BundleAnalyzerPlugin 分析打包后的性能

    目录结构

  • 首先使用npm init 初始化一个包含package.json的根目录

  • :.
    │ .babelrc #babel的规则以及插件
    │ .editorconfig #IDE/编辑器相关的配置
    │ .eslintignore #Eslint忽视的目录
    │ .eslintrc.js #Eslint的规则和插件
    │ .gitignore #Git忽视的目录
    │ .postcssrc.js #postcss的插件
    │ package-lock.json
    │ package.json #项目相关的包
    │ README.md
    │ yarn.lock
    │
    ├─build #webpack相关的配置
    │ utils.js #webpack配置中的通用方法
    │ webpack.base.conf.js #webpack的基础配置
    │ webpack.dev.conf.js #webpack的开发环境配置
    │ webpack.prod.conf.js #webpack的生产环境配置
    │
    └─src #主目录,业务代码
     │ app.css
     │ App.js
     │ favicon.ico
     │ index.ejs
     │ index.js
     │
     └─assets #静态目录,存放静态资源
     │ config.json
     │
     └─img
     logo.svg

    安装依赖

  • eslint-loader

  • eslint

  • eslint-config-airbnb

  • eslint-plugin-import

  • eslint-friendly-formatter

  • eslint-plugin-flowtype

  • eslint-plugin-jsx-a11y

  • eslint-plugin-react

  • babel-polyfill

  • webpack

  • jest

  • friendly-errors-webpack-plugin 编译提示的webpack插件

  • html-webpack-plugin 新建html入口文件的webpack插件

  • copy-webpack-plugin webpack配置合并模块

  • webpack-merge webpack配置合并模块

  • webpack-dev-server

  • webpack-bundle-analyzer

  • webpack-cli

  • portfinder 寻找接口的插件

  • extract-text-webpack-plugin

  • node-notifier

  • optimize-css-assets-webpack-plugin

  • autoprefixer

  • mini-css-extract-plugin

  • autoprefixer

  • css-loader

  • less-loader

  • postcss-loader

  • postcss-import

  • postcss-loader

  • style-loader

  • babel-core

  • babel-eslint

  • babel-loader

  • babel-plugin-transform-runtime

  • babel-plugin-import

  • babel-preset-env

  • babel-preset-react

  • babel-polyfill

  • url-loader

  • cross-env

  • file-loader

  • yarn add eslint eslint-loader eslint-config-airbnb eslint-plugin-import eslint-friendly-formatter eslint-plugin-flowtype eslint-plugin-jsx-a11y eslint-plugin-react babel-polyfill webpack jest webpack-merge copy-webpack-plugin html-webpack-plugin friendly-errors-webpack-plugin webpack-dev-server webpack-bundle-analyzer webpack-cli portfinder extract-text-webpack-plugin node-notifier optimize-css-assets-webpack-plugin autoprefixer mini-css-extract-plugin autoprefixer css-loader less-loader postcss-loader postcss-import postcss-loader style-loader babel-core babel-eslint babel-loader babel-plugin-transform-runtime babel-plugin-import babel-preset-env babel-preset-react babel-polyfill url-loader cross-env file-loader -D

    项目配置

    webpack 基础配置

    1. 为了控制开发环境和生产环境,我们可以新建build文件夹。分别书写开发环境和生产环境的webpack配置文件,这样也更可以方便我们分别控制生产环境和开发环境。

    2. 为了提高代码的复用率,也为了区别 基础配置个性配置 ,可以分别新建webpack.basewebpack.devwebpack.prod三个配置文件。首先配置最基础的entry(入口)和output(出口)。

    module.exports = {
     context: path.resolve(__dirname, '../'), //绝对路径。__dirname为当前目录。
     //基础目录用于从配置中解析入口起点。因为webpack配置在build下,所以传入 '../'
     entry: {
     app: ('./src/index.js') //项目的入口
     },
     output: {
     path: path.resolve(__dirname, '../dist'),
     filename: '[name].[hash:8].js',
     publicPath: '/',
     libraryTarget: 'umd',
     },
    }

    entry

    entry可以分别为字符串、数组和对象。

    倘若应用只有一个单一的入口,entry的值可以使用任意类型,不会影响输出结果。

    // entry为字符串
    {
     entry: './src/index.js',
     output: {
     path: '/dist',
     filename: 'bundle.js'
     }
    }
    // 结果会生成 '/dist/bundle.js'
    // entry为数组,可以添加多个彼此不互相依赖的文件。结合output.library选项,如果传入数组,则只导出最后一项。
    {
     //如果你在html文件里引入了'bable-polyfill',可以通过数组将它加到bundle.js的最后。
     entry: ['./src/index.js', 'babel-polyfill'] ,
     output:{
     path: '/dist',
     filename: 'bundle.js'
     }
    }
    // entry为对象,可以将页面配置为多页面的而不是SPA,有多个html文件。通过对象告诉webpack为每个入口,成一个bundle文件。
    // 多页面的配置,可能还要借助于HtmlWebpackPlugin,来指定每个html需要引入的js
    {
     entry: {
     index: './src/index.js'
     main: './src/index.js'
     login: './src/login.js'
     }
     output:{
     path: '/dist/pages'
     filename: '[name]-[hash:5].js' //文件名取自'entry'对象的键名,为了防止推送代码后浏览器读缓存,故再生成的文件之后加上hash码。
     }
    }
    // 会分别生成index.js,main.js,login.js三个文件

    关于 webpack构建多页面 可以参考这篇文章。不过现在webpack4.x也是一次断崖式升级,感兴趣的同学可以自行搜索。

    // entry也可以传入混合类型
    {
     entry:{
     vendor: ['jquery','amap','babel-polyfill'] //也可以借助CommonsChunkPlugin提取vendor的chunk。
     index: './src/index.js'
     }
     output: {
     path: '/dist'
     filename: '[name]-[hash:5].js'
     }
    }

    CommonsChunkPlugin在webpack4.0之后移除了,可以使用splitChunksPlugin代替。

    output

    output最基础的两个配置为 pathfilename

  • path 告诉 webpack的输出目录在那里,一般我们会设置在根目录的 dist 文件夹;

  • filename 用于指定输出文件的文件名,如果配置了创建了多个单独的 chunk 则可以使用[name].[hash] 这种占位符来确保每个文件有唯一的名称;

  • 另一个常见配置 publicPath 则是用于更加复杂的场景。举例:在本地时,你可能会使用 ../assets/test.png 这种url来载入图片。而在生产环境下,你可能会使用CDN或者图床的地址。那么就需要配置 publicPath = "http://cdn.example.com/assets/" 来实现生产模式下编译输出文件时自动更新url。

  •  output: {
     path: path.resolve(__dirname, '../dist'),
     filename: '[name].[hash:8].js',
     publicPath: '/',
     },

    resolve

    resolve常用的两个配置为 aliasextensions

  • alias 创建import或者require的别名

  • extensins 自动解析文件拓展名,补全文件后缀

  • resolve: {
     // 自动解析文件扩展名(补全文件后缀)(从左->右)
     // import hello from './hello' (!hello.js? -> !hello.jsx? -> !hello.json)
     extensions: ['.js', '.jsx', '.json'],
     alias: {
     '@': resolve('src')
     }
     },

    module

    module的选项决定了如何处理项目中的不同类型的模块。其中常用的有 rulesnoParese 两个配置项。

  • noParese 是为了防止weback解析与所有与rule相匹配的文件。目的是,忽略大型的library可以提高构建性能。

  • noParse: function(content) {
     return /jquery|lodash/.test(content);
    }
  • rules 用于在创建模块是,匹配规则数组,以确定哪些规则能够对module应用loader,或者是修改parser。

  • module: {
     rules: [
     {
     test: /\.(js|jsx)$/,
     exclude: /node_modules/,
     enforce: 'pre',
     use: [{
     loader: 'babel-loader',
     }, {
     loader: 'eslint-loader', // 指定启用eslint-loader
     options: {
     formatter: require('eslint-friendly-formatter'),
     emitWarning: false
     }
     }]
     },
     {
     test: /\.css$/,
     include: /node_modules/,
     use: [
     MiniCssExtractPlugin.loader,
     'css-loader',
     {
     loader: 'postcss-loader',
     options: {
     plugins: () => [autoprefixer({ browsers: 'last 5 versions' })],
     sourceMap: false,
     },
     },
     ],
     },
     {
     test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
     loader: 'url-loader',
     options: {
     limit: 10000,
     name: ('assets/img/[name].[hash:7].[ext]')
     }
     },
     {
     test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
     loader: 'url-loader',
     options: {
     limit: 10000,
     name: ('assets/media/[name].[hash:7].[ext]')
     }
     },
     {
     test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
     loader: 'url-loader',
     options: {
     limit: 10000,
     name: ('assets/fonts/[name].[hash:7].[ext]')
     }
     }
     ]
     }

    例如上述代码,就使用eslint-lodaerbabel-loader 处理了除了node_modules 以外的 js||jsx。同时配置了,解析图片、视频、字体文件等的解析,当rules匹配到的文件时,小于10000 byte 时,采用url-loader解析文件。(因为base会让图片的体积变大,所以当文件较大时,使用base并不明智)

    Webpack开发配置

    因为在webpack 4.X 中使用了流行的 ”约定大于配置“ 的做法,所以在新加入配置项 mode ,可以告知webpack使用相应模式的内置优化。

    选项描述
    development会将process.env.NODE_ENV 的值设为 development 。启用NamedChunksPluginNamedMoudulesPlugin
    production会将process.env.NODE_ENV 的值设为 production 。启用FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPluginOccurrenceOrderPluginSideEffectsFlagPluginUglifyJsPlugin

    如果我们只设置NODE_ENV,则不会自动设置 mode

    在开发时,我们往往希望能看到当前开发的页面,并且能热加载。这时,我们可以借助webpack-dev-server 这个插件,来在项目中起一个应用服务器。

    // package.json
    "scripts": {
     "start": "webpack-dev-server --mode development --config build/webpack.dev.conf.js",
    }
    // 设置当前的mode为development,同样这个配置也可以写在webpack.dev.conf.js中。然后使用build目录下的webpack.dev.conf.js 来配置相关的webpack。
    devServer: {
     clientLogLevel: 'warning',
     historyApiFallback: true, //在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
     contentBase: path.resolve(__dirname, '../src'),
     compress: true,
     hot: true, // 热加载
     inline: true, //自动刷新
     open: true, //自动打开浏览器
     host: HOST||'localhost',
     port: PORT,
     overlay: { warnings: false, errors: true }, // 在浏览器上全屏显示编译的errors或warnings。
     publicPath: '/',
     proxy: {},
     quiet: true, // necessary for FriendlyErrorsPlugin // 终端
    输出的只有初始启动信息。 webpack 的警告和错误是不输出到终端的 watchOptions: { poll: false } }, plugins: [ new webpack.DefinePlugin({ ...process.env }), //开启HMR(热替换功能,替换更新部分,不重载页面!) new webpack.HotModuleReplacementPlugin(),// HMR shows correct file names in console on update. //显示模块相对路径 new webpack.NamedModulesPlugin(), //不显示错误信息 new webpack.NoEmitOnErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin ]

    其实在开发时,我们可以设置 contentBase: '/src'contentBase 指定了devServer能访问的资源地址。因为我们开发时,资源大部分都放在src目录下,所以可以直接指定资源路径为src目录。因为我们在webpack基础配置时,配置了 output 输出为 dist 目录,所以我们也可以在devServer里,设置 contentBasedist 目录。不过此时需要使用copyWebpackPlugin将一些静态资源复制到 dist 目录下,手动新建dist目录,并复制也可以。

    另外,当使用 history 路由时,要配置 historyApiFallback = true ,以此让服务器放弃路由权限,交由前端路由。而是用 hash 路由则不需要此配置。

    项目进阶

    生产环境配置

    在使用webpack 4.x 的 mode 配置之后,需要我们手动配置的项已经减少了很多,像js代码压缩这种工具 UglifyJsPlugin 就已经不用手动去配置。但是像很多前面提到的 代码分离css代码提取和压缩html的生成 以及 复制静态资源 还需要我们手动配置。

    代码分离

    // 设置代码分离的
    输出目录 output: { path: path.resolve(__dirname, '../dist'), filename: ('js/[name].[hash:8].js'), chunkFilename: ('js/[name]-[id].[hash:8].js') }, // 代码分离 optimization: { runtimeChunk: { name: "manifest" }, splitChunks: { chunks: 'all' } },

    css代码压缩

    借助 MiniCssExtractPlugin 来实现压缩css和提取css。因为 MiniCssExtractPlugin 无法与style-loader 共存,所以我们需要判断当前环境是生成环境还是开发环境。

    我们可以新建一个util.js的文件,在webpack当中一些共用的方法。考虑使用个别配置字段 extract 来配置使用何种方式来配置css-loader。参见 util.js 代码。

    new MiniCssExtractPlugin({
     filename: 'css/[name].[hash:8].css',
     chunkFilename: 'css/[name]-[id].[hash:8].css',
     }),

    生成HTML

    使用htmlWebpackPlugin,配合ejs。可以使控制html 的生成。通过配置的方式,生成html。因为 HtmlWebpackPlugin 本身可以解析ejs,所以不需要单独引入ejs的loader。

    new HtmlWebpackPlugin({
     filename: 'index.html',
     template: './src/index.ejs', // 设置目录
     title: 'React Demo',
     inject: true, // true->'head' || false->'body'
     minify: {
     //删除Html注释
     removeComments: true,
     //去除空格
     collapseWhitespace: true,
     //去除属性引号
     removeAttributeQuotes: true
     // more options:
     // https://github.com/kangax/html-minifier#options-quick-reference
     },
     // necessary to consistently work with multiple chunks via CommonsChunkPlugin
     chunksSortMode: 'dependency'
     }),
    <!DOCTYPE html>
    <html>
    <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="robots" content="noindex, nofollow">
    
     <title><%= htmlWebpackPlugin.options.title %></title>
     <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
     <link rel="icon" href="/favicon.ico" type="image/x-icon">
    
     <% for (var chunk in htmlWebpackPlugin.files.css) { %>
     <link rel="preload" href="<%= htmlWebpackPlugin.files.css[chunk] %>" as="style">
     <% } %>
     <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
     <link rel="preload" href="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>" as="script">
     <% } %>
    
     <base href="/">
    </head>
    <body>
    <p id="root"></p>
    </body>
    <style type="text/css">
     body {
     font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif;
     }
    </style>
    </html>

    复制静态目录

    将所以可能被请求的静态文件,分别放在assets目录下。那么在打包后,为了保证目录能正常访问(不使用CDN等加载静态资源时),我们可以配置 publicPath = '/' 。然后借助于 CopyWebpackPlugin 实现资源复制。

    new CopyWebpackPlugin([{
     from: './src/assets/',
     to: 'assets'
     }]),

    src/assets 复制到 dist/assets 目录下

    开启打包分析

    借助插件 BundleAnalyzerPlugin 直接在plugins中创建该插件:

    // webpack.prod.conf.js
    const BundleAnalyzerPlugin = process.env.NODE_ENV=== 'analysis' ? require('webpack-bundle-analyzer').BundleAnalyzerPlugin:null
    process.env.NODE_ENV=== 'analysis' ? new BundleAnalyzerPlugin() : ()=>{}

    在package.json 中可做如下配置:

    "scripts": {
     "analysis": "cross-env NODE_ENV=analysis webpack -p --mode production --progress --config ./build/webpack.prod.conf.js ",
     },

    通过注入环境变量,来控制是否运行打包分析。

    ssh部署

    打包后的dist文件夹,可以直接借助 node 的 ssh-node ,直接部署到服务器指定的目录下。 ssh-node既支持ssh,也支持密码登录。建议可以为在每个项目下,新建一个.ssh文件,存放项目的私钥。代码如下:

    // usage: https://www.npmjs.com/package/node-ssh
    var path, node_ssh, ssh, fs, opn, host
    
    fs = require('fs')
    path = require('path')
    node_ssh = require('node-ssh')
    opn = new require('opn')
    ssh = new node_ssh()
    host = 'localhost'
    var localDir = './dist'
    var remoteDir = '/opt/frontend/new'
    var removeCommand = 'rm -rf ./*'
    var pwdCommand = 'pwd'
    
    ssh.connect({
     host: host,
     username: 'root',
     port: 22,
     // password,
     privateKey: "./.ssh/id_rsa",
    })
     .then(function() {
     ssh.execCommand(removeCommand, { cwd:remoteDir }).then(function(result) {
     console.log('STDOUT: ' + result.stdout)
     console.log('STDERR: ' + result.stderr)
     ssh.putDirectory(localDir, remoteDir).then(function() {
     console.log("The File thing is done")
     ssh.dispose()
     opn('http://'+host, {app:['chrome']})
     }, function(error) {
     console.log("Something's wrong")
     console.log(error)
     ssh.dispose()
     })
     })
     })

    此时,在命令行直接 node deploy.js 就可以运行以上脚本,我们也可以添加一个build + deploy的script脚本,便于启动。

    "scripts": {
     "depoly": "npm run build && node ./deploy.js",
    }

    相关推荐:

    详解React 快速上手脚手架 create-react-app

    简单搭建一个react项目

    文档

    如何基于webpack4搭建一个react脚手架的过程分析

    如何基于webpack4搭建一个react脚手架的过程分析:本篇文章分享给大家的内容是关于如何基于webpack4搭建一个react脚手架的过程分析,内容很详细,接下来我们就来看看具体的内容,希望可以帮助到有需要的朋友。react-sample-javascriptReact 16.0 boilerplate with react-router-do
    推荐度:
    标签: 步骤 搭建 React
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top