手摸手: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. 欢迎关注公众号「前端进阶课」认真学前端,一起进阶。

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