手摸手:3秒打包一个three.js项目(有物证)

关于 webpack 相关的文章太多了,何不一起从零开始手写一个配置呢?

真的3秒能打包一个three.js项目吗?真的,后面会提供源文件地址哦。

要打包的项目是这个样子的。


image

从零开始

关于 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. 欢迎关注公众号「前端进阶课」认真学前端,一起进阶。
image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容