首頁  >  文章  >  web前端  >  如何基於webpack4搭建一個react腳手架的流程分析

如何基於webpack4搭建一個react腳手架的流程分析

不言
不言原創
2018-07-26 11:59:082457瀏覽

這篇文章分享給大家的內容是關於如何基於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
  1. url-loader
  2. #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 基礎設定############為了控制開發環境和生產環境,我們可以新建build資料夾。分別書寫開發環境和生產環境的webpack配置文件,這樣也更可以方便我們分別控制生產環境和開發環境。 ############為了提高程式碼的複用率,也為了區別###基礎配置### 和###個性配置### ,可以分別新建###webpack.base ###、###webpack.dev### 和###webpack.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最基礎的兩個配置為###path### 和###filename### :############ ##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解析文件。(因为base64会让图片的体积变大,所以当文件较大时,使用base64并不明智)

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: &#39;Source Sans Pro&#39;,&#39;Helvetica Neue&#39;,Helvetica,Arial,sans-serif;
  }
</style>
</html>

复制静态目录

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

new CopyWebpackPlugin([{
      from: &#39;./src/assets/&#39;,
      to: &#39;assets&#39;
    }]),

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

开启打包分析

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

// webpack.prod.conf.js
const BundleAnalyzerPlugin = process.env.NODE_ENV=== &#39;analysis&#39; ? require(&#39;webpack-bundle-analyzer&#39;).BundleAnalyzerPlugin:null
process.env.NODE_ENV=== &#39;analysis&#39; ? 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(&#39;fs&#39;)
path = require(&#39;path&#39;)
node_ssh = require(&#39;node-ssh&#39;)
opn = new require(&#39;opn&#39;)
ssh = new node_ssh()
host = &#39;localhost&#39;
var localDir = &#39;./dist&#39;
var remoteDir = &#39;/opt/frontend/new&#39;
var removeCommand = &#39;rm -rf ./*&#39;
var pwdCommand = &#39;pwd&#39;

ssh.connect({
  host: host,
  username: &#39;root&#39;,
  port: 22,
  // password,
  privateKey: "./.ssh/id_rsa",
})
  .then(function() {
    ssh.execCommand(removeCommand, { cwd:remoteDir }).then(function(result) {
      console.log(&#39;STDOUT: &#39; + result.stdout)
      console.log(&#39;STDERR: &#39; + result.stderr)
      ssh.putDirectory(localDir, remoteDir).then(function() {
        console.log("The File thing is done")
        ssh.dispose()
        opn(&#39;http://&#39;+host, {app:[&#39;chrome&#39;]})
      }, function(error) {
        console.log("Something&#39;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腳手架的流程分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn