手摸手:3秒打包一個three.js項目(有物證)

關於 webpack 相關的文章太多了,何不一起從零開始手寫一個配置呢?

真的3秒能打包一個three.js項目嗎?真的,後面會提供源文件地址哦。

要打包的項目是這個樣子的。

從零開始

關於 three.js 的安裝和使用部分都省略。

首先是最基礎的。我們需要安裝

  1. cross-env 目前最流行的運行跨平臺設置和使用環境變量的腳本
  2. webpack + webpack-cli + webpack-dev-server:三'賤'客,項目必備

參考常規webpack配置結構需要3個最基礎文件:

  1. webpack 基礎配置文件,暫命名爲 webpack.base.js
  2. webpack 開發配置文件,暫命名爲 webpack.dev.js
  3. webpack 打包配置文件,暫命名爲 webpack.prod.js

當然,需要把 devprod 中的配置和 base 的配置合併起來,安裝個webpack-merge 吧。

然後配置一下最熟悉的腳本運行環節吧。通過--config來對標配置文件,通cross-env 設置環境變量

"dev": "cross-env NODE_ENV=dev webpack-dev-server --config script/webpack.dev.js",
"build": "cross-env NODE_ENV=prod webpack --config script/webpack.prod.js"

好的,初期準備工作都OK開始配置環節。

開始配置

首先是webpack的出入口。出口設置爲 dist 環節簡單直接上代碼。

  {
    entry: './src/index.js',
    output: {
      filename: '[name].[hash:8].js',
      path: rootResolve('dist'),
      publicPath: '/'
    },
  }

順便配置下別名。依然可以直接上代碼

  resolve: {
    extensions: ['.js', '.json'],
    alias: {
      '@': rootResolve('src'),
      '@assets': rootResolve('src/assets'),
    }
  }

然後是關鍵環節:loaderplugins

關於 loader:

  • 樣式上使用 less
    • 需要通過less-loader 解析 less 因爲 webpack 只能讀懂js
    • 解析完成再通過 postcss-loader 加上瀏覽器前綴
    • 再通過css-loader 解析css代碼中的 url@import語法
    • 最後,通過MiniCssExtractPlugin.loader 生成 .css文件
  • JS 文件使用 babel-loader,關於 babel 文章太多了,暫略
    • 順便使用 HappyPack 進行優化加速
    • 爲什麼不選 thread-loader 呢? (因爲名字不好聽 - -! 怪我咯)
  • 其他文件,用 url-loader 咯。

然後 loader 配置就是這樣的

{
  test: /\.less$/,
  exclude: /(node_modules|bower_components)/,
  loaders: [{
    loader: MiniCssExtractPlugin.loader,
    options: {
      esModule: true,
      hmr: process.env.NODE_ENV === 'dev', // 熱更新
      // publicPath: '../',
    }
  }, 'css-loader', 'postcss-loader', 'less-loader']
},
{
  test: /\.m?js$/,
  exclude: /(node_modules|bower_components)/,
  loader: 'happypack/loader',
  options: {
    id: 'babel',
  }
},
{
  test: /\.(png|jpe?g|gif)(\?.*)?$/,
  use: [{
    loader: 'url-loader',
    options: {
      limit: 8192,
      name: 'assets/img/[hash:8].[ext]'
    }
  }]
}

關於插件部分,首先是配合上面 loader的相關插件:HappyPackMiniCssExtractPlugin

new MiniCssExtractPlugin({
  filename: "css/[name].[hash:8].css", // css 路徑
}),
new HappyPack({
  id: 'babel',
  loaders: [{
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env'],
      cacheDirectory: true
    }
  }]
})

當然,我想知道運行和打包的進度: ProgressPlugin,順便弄個 DefinePlugin 工程化必備插件。最後webpack生成後的代碼注入不能少了 HtmlWebpackPlugin

然後 base 文件的插件結構是這樣的

plugins: [
  new webpack.ProgressPlugin(),
  new webpack.DefinePlugin({
    NODE_ENV: JSON.stringify(process.env.NODE_ENV), // 當前使用環境
    VERSION: JSON.stringify('0.1.0'),
  }),
  new MiniCssExtractPlugin({
    filename: "css/[name].[hash:8].css", // css 路徑
    // chunkFilename: "[id].css",
  }),
  new HappyPack({
    id: 'babel',
    loaders: [{
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        cacheDirectory: true
      }
    }]
  }),
  new HtmlWebpackPlugin({ template: './src/index.html' })
]

開發環境配置

首先開發環境 api 代理必不可少。那麼就是 devServer.proxy了,順便再定義下開發環境端口號。

devServer: {
  contentBase: path.join(__dirname, "dist"),
  compress: true,
  port: 3333
}

目前也沒有太多事情,那麼 merge 下再配個 HotModuleReplacementPlugin

merge(base, {
  mode: 'development',
  plugins: [
  ],
  devServer: {
    contentBase: rootResolve("src"),
    compress: true,
    port: 3333
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
})

打包環境

打包環境主要做了這幾件事情

  1. 打包優化
  2. 分類文件
  3. 刪除冗餘

首先是 dll

  1. 定義 dll 配置文件。 比如:webpack.dll.config.js
    1. 需要定義要打包的庫和打包的出口
    2. 命名生成後的dll模塊的詳細要點文件 manifest.dll.json

那麼 webpack.dll.config.js 內容應該是這樣的

{
    // 你想要打包的模塊的數組
    entry: {
        vendor: ['three']
    },
    output: {
        filename: '[name].dll.js',
        path: distResolve('dll'), // 打包後文件輸出的位置
        library: '[name]_library'
        // 這裏需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
    },
    plugins: [
        new webpack.DllPlugin({
            name: '[name]_library',
            path: distResolve('dll/manifest.dll.json'),
            context: __dirname
        })
    ]
}
  1. 通過 DllReferencePlugin + json文件 把 dll模塊的詳細要點告訴 webpack

prod 文件中添加 plugins

new webpack.DllReferencePlugin({
  context: __dirname,
  manifest: require(distResolve('./dll/manifest.dll.json'))
})
  1. 添加腳本運行配置
"dll": "webpack --config script/webpack.dll.config.js",

運行下 npm run dll,在 dist/dll 目錄下生成dll相關文件,那麼 dll 配置也完成了。順便做一些清理工作,用下 CleanWebpackPlugin

new CleanWebpackPlugin({
  cleanOnceBeforeBuildPatterns: [
    'assets', 'js', 'css', 'index.html', '*.js',
    '!manifest.dll.json', '!vendor.dll.js' // 不刪除 dll 文件
  ],
})

然後是代碼優化,其實當 mode: 'production' 時已經做了很多代碼優化相關的事情了。(我不管,我就是要優化 - -!

做一下 js的並行壓縮吧

optimization: {
  minimizer: [
    new TerserWebpackPlugin({
      parallel: true,  // 啓用並行壓縮
      cache: true,    // 啓用緩存
    }),
    new OptimizeCssAssetsPlugin({ // 壓縮css
      cssProcessorOptions: {
        safe: true
      }
    })
  ],
  runtimeChunk: true, // 自動拆分runtime文件
  splitChunks: {
    chunks: 'async',
    minSize: 30000,
    automaticNameDelimiter: '~',
    automaticNameMaxLength: 30,
    cacheGroups: {
      defaultVendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: -10
      },
      default: {
        minChunks: 2,
        priority: -20,
        reuseExistingChunk: true
      }
    }
  },
}

歐耶,再配置下js的打包後路徑就好了

output: { // JS 路徑
  path: distResolve(),
  filename: 'js/[id].[chunkhash].js',
  chunkFilename: 'js/[name].[chunkhash].js'
},

最後 mergebase 配置。在 dev 時做過了... 省略。

至此,Webpack配置已經大部分完成了,運行npm run build打包代碼,1、2、3。 3秒打包完成了。

爲什麼只需要3秒呢?雖然上面的配置確實做了很多優化,但是大部分事情都被表象迷惑了,具體爲何下一章見。

最後

  1. 源碼地址 https://github.com/zhongmeizhi/three-demo
  2. 更多實戰項目:https://github.com/zhongmeizhi/z-ui
  3. 一個字一個字碼出來的文章,原創不易,點個讚唄。
  4. 歡迎關注公衆號「前端進階課」認真學前端,一起進階。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章