webpack4打包nodejs项目进阶版——多页应用模板

前段时间我写了个打包nodejs项目的文章,点击前往css

可是,问题不少。由于以前的项目是个历史遗留项目,重构起来可能会爆炸,当时又比较急因此就写个的适用范围很小的webpack的打包方法。html

最近稍微得空,便动了重构的心思,重构第一步固然要把架子搭起来前端

而搭架子的过程也是十分地艰辛啊,终于大概搞定了前端的部分,这一次就分享一下使用最新的webpack4怎么打包nodejs的多页应用node

欢迎大佬留言交流,想要源码的点此前往githubjquery

 

工程目录

走个流程先上个项目结构图webpack

 

这里先说明一下,为何除了webpack.config.js这个配置文件以外还有一个config文件夹存放相关配置文件git

由于webpack分为了开发环境和生产环境,二者在配置和表现形式上有所区别,放在一个文件中不利于维护github

这也算是一种解耦吧。web

至于其余的一些文件我这里就大概提一下:express

1.babelrc 配置babel-loader 用于将ES6+的JS代码转为ES5的通用JS

2.eslint 主要用于代码的在线纠错,以及一些语法错误的查找

3.用于 git 的配置配置哪些文件须要上传到git

4. package.json就是用于设置项目信息,以及项目的依赖

5.postcss 用于配置postcss 主要用于修复浏览器兼容的问题

6, yarn 就是一个进阶版的npm 能够并行下载 缓存等(由facebook 研发)

 

以上就是整个架子的大概模板

接下来进入主题——webpack的相关配置

 

cross-env跨平台设置环境变量

经过cross-env 来判断当前的环境(即生产环境、开发环境)

用法以下:

 

 在package.json中设置启动命令

将 NODE_ENV 设置为不一样的值

根据该值来判断当前的环境

 

Webpack.config.js

一般来讲该文件就是webpack 的核心配置文件

但为下降不一样环境的耦合度,使代码逻辑更加清晰

我使用这个文件做为一个“路由” 根据以前的 NODE_ENV 去请求不一样的webpack配置文件

代码以下:

为了兼容VUE等框架因此个人ESlint 设为不以分号结尾

 

config文件夹

我全部的webpack配置文件夹都存放在该文件夹下

上方要获取的配置文件都在这里

个人想法是在base.js 中存放两种环境的公共代码

dev.js、prod.js 存放对应环境的特殊配置代码

最后输出的文件只能有一个webpack的配置文件

因此使用

webpack-merge

来合并两个webpack配置文件

 

webpack基础配置

下面咱们来一 一分析每一个配置文件

首先就是base.js

代码以下:

/**
 * webpack 基础配置
 */
const webpack = require('webpack')

const path = require('path')

const fs = require('fs')

const Entries = {} // 保存文件入口
const pages = []// 存放html-webpack-plugin实例
const env = process.env.NODE_ENV !== 'prod' // 判断运行环境
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入mini-css-extract-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');

//  获取html-webpack-plugin实例集合
(function () {
  let pagePath = path.join(__dirname, '../src/page')// 定义存放html页面的文件夹路径
  let paths = fs.readdirSync(pagePath) // 获取pagePath路径下的全部文件
  paths.forEach(page => {
    page = page.split('.')[0]// 获取文件名(不带后缀)
    pages.push(new HtmlWebpackPlugin({
      filename: `views/${page}.html`, // 生成的html文件的路径(基于出口配置里的path)
      template: path.resolve(__dirname, `../src/page/${page}.html`), // 参考的html模板文件
      chunks: [page, '[name]', 'commons', 'vendors', 'manifest'], // 配置生成的html引入的公共代码块 引入顺序从右至左
      favicon: path.resolve(__dirname, '../src/img/favicon.ico'), // 配置每一个html页面的favicon
      minify: {// 配置生成的html文件的压缩配置
        collapseWhitespace: true,
        collapseInlineTagWhitespace: true,
        conservativeCollapse: true,
        minifyCSS: true,
        minifyJS: true,
        removeComments: true,
        trimCustomFragments: true
      }
    }))
    Entries[page] = path.resolve(__dirname, `../src/js/${page}.js`)// 入口js文件
  })
})()

module.exports = {
  // 配置入口文件
  entry: Entries,
  // 启用 sourceMap
  devtool: 'cheap-module-source-map',
  // mode为none表示这是默认配置
  mode: 'none',
  // 配置文件出口
  output: {
    // 将打包好的js输出到public(静态资源目录)下的js文件夹中
    filename: 'public/js/[name].bundle.[hash].js',
    path: path.resolve(__dirname, '../dist'), // 输出目录,全部文件的输出路径都基于此路径之上(须要绝对路径)
    publicPath: '../'
  },
  // 省略文件后缀
  resolve: {
    extensions: ['.js'] // 配置事后,书写该类文件路径的时候能够省略文件后缀
  },
  // loader
  module: {
    rules: [
      // 使用expose处理JQuery(JQ使用npm安装)配置了这一条后就不要使用external(主要用于cdn引入)
      {
        test: require.resolve('jquery'), // 此loader配置项的目标是NPM中的jquery
        loader: 'expose-loader?$!expose-loader?jQuery' // 先把jQuery对象声明成为全局变量`jQuery`,再经过管道进一步又声明成为全局变量`$`
      },
      // 处理html中的图片,考虑到node使用模板的状况因此不能使用html-loader
      {
        test: /\.html$/,
        use: [{
          loader: 'html-withimg-loader' // 处理img标签中的图片
        }]
      },
      // 处理样式表
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          env ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(less)$/,
        use: [
          env ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      // 使用babel处理js文件
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: 'babel-loader'
      },
      // 处理图片
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 10000, // 设置图像大小超过多少转存为单独图片
            name: 'public/img/[name].[hash].[ext]' // 转存的图片目录
          }
        }]
      },
      // 处理字体
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['url-loader']
      }
    ]
  },
  // 配置插件
  plugins: [
    // 分离tml-webpack-plugin实例数组、引入jq
    ...pages, new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.$': 'jquery',
      'window.jQuery': 'jquery'
    })
  ],
  // 配置webpack执行相关
  performance: {
    maxEntrypointSize: 1000000, // 最大入口文件大小1M
    maxAssetSize: 1000000 // 最大资源文件大小1M
  }
}

关于上述代码

首先咱们要配置的是入口:

我这里使用一个函数来遍历page文件夹中的全部html文件

这里咱们约定对应html的js与html同名以便咱们自动化生成入口对象

以下图所示:

这样咱们就能使用html的名字来设置入口了,获取的入口对象以下:

该函数的另外一个功能就是,根据html文件使用

html-webpack-plugin

来自动生成咱们的html页面

 

看到这里或许有的小伙伴会有疑问,为啥不用html-loader来解析html文件而后打包进去?

正好我也解答一下一些,node项目中使用ejs等模板的小伙伴的疑问,不是html怎么办?

 

缘由以下:

1.我这个架子主要考虑的是node项目,一般来讲node不论是作中间层,仍是作全栈都有可能会使用模板引擎

而html-loader没法解析ejs等模板语法

2.以ejs来举例,若是我使用ejs-loader来解析呢?若是使用ejs-loader那么只能适用于用ejs作组件化开发的

状况,而不能适用于使用ejs作数据渲染(中间层)的状况

3.那么在已是ejs等模板的状况下的node项目怎么使用个人架子呢?

答案很简单,在app.js中加入如下代码(express),若仍是不懂参考我上一篇初级版的webpack

 

 

4 从另外一个方面来讲将ejs等文件改成html文件有利于搜索引擎优化(小声哔哔)

 

关于入口和html的问题就解答到这

下一步咱们就应该配置出口了

话很少说先上代码:

若是是搞node的小伙伴应该知道public(静态资源目录)

因此我将webpack打包后的文件输出到该目录下

关于publicpath 我这里用的相对路径,就是让webpack-server 的项目根路径和个人静态资源文件一致 否则 run dev 的时候会404

 

在这里提一下JQuery的问题,目前来讲jq有三种引入方式

1.cdn 引入

2.import 本地文件

3.expose-loader 暴露出 npm 安装的jquery

 

这里我采用的是第三种方法

有几个好处

1.在页面中不用显式地引入jq了 (懒是人类进步的第一辈子产力)

2.使jq也归入了npm模块化管理的范畴

3. 前面两点足够了,emm

 

代码以下

 

说完了jq的问题而后就是配置不一样文件的loader了

 

 详细代码在个人github上,帮到大家的小伙伴,跪求星星

 

基础配置中还有一件事

那就是performance

webpack默认入口点文件不能超过300k

超事后webpack会报warning

没有强迫症的小伙伴能够跳过了

有两个解决办法:

1.关掉webpack的警告(一看就不能选)

2.设置performance

设置以下:

 

好了基本配置就完成了

接下来要针对,不一样环境进行独立的配置

 

开发环境配置

我先讲开发环境的配置,生产环境的坑有点多放到最后讲

对于开发环境来讲,代码会常常修改并且,咱们须要频繁地查看样式,因此咱们并不须要对文件进行压缩等处理

而且要让它可以热更新便可,这里咱们使用webpack-server

配置代码以下:

这里没啥要注意的,直接按着配,run就行运行出来像下面这样

页面以下:

具体的我就不演示了

仍是那句话github见

 

接下来开始重头戏生产环境的配置

 

 

生产环境

为啥是重头戏呢?生产环境那就是线上环境啊,效率、大小就是钱啊

另外呢,主要是webpack4 和 min-css的配合有点问题,我这搭架子的时候搞的我头皮发麻

我不太清楚这是bug仍是个人操做有啥问题

 

好了,进入正题

关于生产环境,主要的配置是:

1.要可以删除以前的过时文件,手动删多low啊

2.要压缩代码,用webpack的目的是啥,除了构建自动化的前端工做流以外,最主要的目的无非是压缩代码嘛

 

压缩代码的好处我这里就不说了,网上一搜一堆

好了开搞

首先清理过时代码:

 

这一步就完成了

下一步抽离css样式

这里要说一下,webpack4中抽离css要使用

mini-css-extract-plugin

原来的那个在webpack4不能使用

 

这里我要吐槽一下官网给的示例,坑了我一下

这里的两个属性是相似域output中的同名属性的,通常来讲只用配置一个就行

 

另外可能就是这个插件有点bug

我先说一下我但愿达到的效果

我但愿将每一个html的全部css做为一个单独文件

最好再将css的重复代码提取一下

若是不将css提取成一个单独的文件就无法CDN加速了啊

 

可是问题来了无法提取公共css代码,网上有的说用Extractcss那个插件的@next能够搞,我试了一下只能不重复打包,不能提取公共代码

我以为人家既然专门为webpack4新出了一个,应该是有过人之处的,因此我就没有用这个方法

 

我就本身开始折腾,我试着用那个提取js重复代码的

splitChunks

我试了一下居然能够处理css,可是有个问题,生成的公共CSS无法自动引入html页面

由于splitChunks是处理js的无法自动引入css

若是实在有提取公共css需求的小伙伴,页面又很少的状况(指你愿意手动引入)

不妨试试这种方法

 

主要步骤以下

在spplitChunks中建立缓存组过滤掉全部的js文件

 

而后再建一个优先级很低的缓存组,将剩下的文件中后缀为css的文件都强制提取到该组

用enforce:true 就能够提取出来,因为不是本文主题,也不知道是否是个bug,感兴趣的小伙伴能够留言我私聊,这里就不过多去讲了

 

 

继续来讲,我这提不提取公共css影响不大

因此个人代码以下:

/**
 * 生产环境配置
 */

const webpackBase = require('./webpack.config.base') // 引入基础配置
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') //  提取css
const webpackMerge = require('webpack-merge') // 引入 webpack-merge 插件
const CleanWebpackPlugin = require('clean-webpack-plugin') // 清理dist文件夹

// 合并配置文件
module.exports = webpackMerge(webpackBase, {
  plugins: [
    new MiniCssExtractPlugin({// 提取出的Css的相关配置
      filename: 'public/css/[name].[hash].css' // 文件存放路径
    }),
    new CleanWebpackPlugin(['dist'], {// 自动清理 dist 文件夹
      root: path.resolve(__dirname, '../'), // 根目录
      verbose: true, // 开启在控制台输出信息
      dry: false // 启用删除文件
    })
  ],
  optimization: {
    minimize: true,
    splitChunks: {// 配置提取公共代码
      chunks: 'all',
      minSize: 30000, // 配置提取块的最小大小(即不一样页面之间公用代码的大小)
      minChunks: 3, // 最小共享块数,即公共代码最少的重复次数通常设为3
      automaticNameDelimiter: '.', // 生成的名称指定要使用的分隔符
      cacheGroups: {// 设置缓存组
        vendors: {
          name: 'vendors',
          test (module) {
            let path = module.resource
            return /[\\/]node_modules[\\/]/.test(path) || /[\\/]lib[\\/]/.test(path)
          },
          priority: 30
        },
        commons: {
          name: 'commons',
          test: /\.js$/,
          enforce: true,
          priority: 20
        }
      }
    },
    runtimeChunk: {
      name: 'manifest' // 打包运行文件
    }
  }
})

 

这里我为js设置了两个缓存组,并提取出了运行时的manifest

一个是依赖的插件等js(知足3个页面引用)生成 vender.js

不知足3个或本身写的js提取到commons.js中

 

结语

以上就是webpack4 打包 nodejs 项目的架子,若是帮到你的小伙伴能够,关注我、收藏走一波

须要代码的小伙伴请移步github,原创不易,望支持

github连接

顺便再给个人JS高编读书笔记系列文章打个广告

有什么问题,欢迎留言,也欢迎大佬指正,共同进步。

相关文章
相关标签/搜索