webpack4打包vue前端多页面项目

之前一直用的脚手架,这次自己搭建webpack前端项目,花费了不少心思,于是做个总结。

1.用法

项目结构如下:

project
 |- bulid          <!-- 这个目录是自动生成的-->
    |- public
    |- css
    |- js
    |- page1.html       <!-- 插件生成的html文件-->
    |- page2.html       <!-- 插件生成的html文件-->
    ...
 |- public/         <!-- 存放字体、图片、网页模板等静态资源-->
 |- src           <!-- 源码文件夹-->
    |- components/
    |- css/
    |- js/
    |- page1.js        <!-- 每个页面唯一的VUE实例,需绑定到#app-->
    |- page2.js        <!-- 每个页面唯一的VUE实例,需绑定到#app-->
    ...
 |- package.json
 |- package-lock.json
 |- README.md

public文件夹存放一些静态文件,src文件夹存放源码。每个页面通过一个入口文件(page1.js,page2.js,..)生成vue实例,挂载到插件生成的html文件的#app元素上。

安装依赖

$ npm install

进入开发模式

$ npm run start

浏览器会打开 http://localhost:3000 ,这时页面一片空白,显示 cannot get几个字。不要慌,在url后面加上 /page1.html ,回车,便可看见我们的页面。 这是因为我把开发服务器的主页设置为 index.html ,而本例中页面为 page1.html,page2.html,因此会显示一片空白。

开发完成了,构建生产版本:

$ npm run build

这会产生一个build/文件夹,里面的文件都经过优化,服务器响应的资源,就是来自于这个文件夹。

2.介绍

2.1 webpack基础配置

我们的开发分为生产环境和开发环境,因此需要有2份webpack的配置文件(可能你会想用env环境变量,然后用3目运算符根据env的值返回不同值。然而这种方法在webpack导出模块的属性中无效,我试过~~~)。这里我们拆分成3个文件,其中 webpack.common.js 是常规的配置,在两种环境下都会用到, webpack.dev.js 和 webpack.prod.js 则是在2种环境下的特有配置。这里用到 webpack-merge 这个包,将公共配置和特有配置进行合成。

webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const devMode = process.env.NODE_ENV !=='production';
// 需要被打包入口文件数组
// 数组元素类型 {string|object}
// string:将以默认规则生成bundle
// object{filename|title|template} 生成的bundle.html的文件名|title标签内容|路径 /public 下的模板文件(需指定文件后缀)
const entryList = [
  'page1',
  'page2',
];
/**
 * @param {array} entryList
 * @param {object} option:可选 要手动配置的内容
 */
const createEntry = (list = [], option = {}) => {
  const obj = {};
  list.forEach((item) => {
    const name = item.filename ? `./js/${item.filename}` : `./js/${item}`;
    obj[name] = path.resolve(__dirname, './src', `./${item}.js`);
  });
  return Object.assign(obj, option);
};
module.exports = {
  entry: createEntry(entryList),
  output: {
    path: path.resolve(__dirname, './build'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.vue$/,
        use: 'vue-loader',
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: 'public/fonts/[name].[ext]',
          },
        },
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: 'public/images/[name].[ext]',
          },
        },
      },
    ],
  },
  plugins: createPluginInstance(entryList).concat([
    // vue SFCs单文件支持
    new VueLoaderPlugin(),
  ]),
};

这里我们没有进行css文件的配置,是因为生产环境下需要优化、提取,所以在另外2个文件分别配置。

webpack.dev.js
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  output: {
    filename: '[name].js',
    chunkFilename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /\.(css|less)$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ],
      },
    ],
  },
  resolve: { alias: { vue: 'vue/dist/vue.js' } },
});

vue分为开发版本和生产版本,最后一行是根据路径指定使用哪个版本。

webpack.prod.js

const webpack = require('webpack');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'production',
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js',
  },
  resolve: { alias: { vue: 'vue/dist/vue.min.js' } },
  module: {
    rules: [
      {
        test: /\.(css|less)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ],
      },
    ],
  },

在production环境下,我们使用了哈希值便于缓存,以后往生产环境下添加其他资源都会如此。

2.2 解决文件输出目录

我们期待的build文件夹具有如下结构:

build
  |- css/
  |- js/
  |- page1.html
  |- page2.html
  ...

即文件按照类型放在一起,html文件直接放在该目录下,可是我们上面的配置的输出结果是混合在一起的。由于name属性既可以是文件名,也可以是 /dir/a 之类带有路径的文件名,我们根据这个特点做出一些修改。

直接对output的输出路径更改

比如改为 build/js ,其他资源利用相对路径比如 ../page1.html 进行修改。我一开始就这样做的,但最终会导致开发服务器无法响应文件的变化,因为他只能针对输出目录下的文件进行监听,该目录之上的文件变化无能为力。

修改入口名称

这也是我们的最终解决方案。将原来的文件名 page1 修改为 /js/page1 ,最终输出的js文件便都会放在js文件夹里。在生产环境下我们通过 MiniCssExtractPlugin 这个插件提取js文件中的css,这是该插件的配置:

new MiniCssExtractPlugin({
      filename:'[name].[contenthash].css'
    })

这里的name就是当初入口的名字,受到入口名称更改的影响,上面最终会变成 js/page1.131de8553ft82.css ,并且该占位符[name]只在编译时有效,这意味着无法用函数对该值进行处理。因此不能使用[name]占位符达到想要的目的,干脆只用[id]。

new MiniCssExtractPlugin({
      filename:'/css/[id].[contenthash].css'
    })

3.代码分割

在webpack4中使用optimization.splitChunks进行分割.

//webpack.common.js
const path = require('path');
module.exports = {
  // ... 省略其他内容
  optimization:{
    runtimeChunk:{
      name:'./js/runtime'
    },
    splitChunks:{
      // 避免过度分割,设置尺寸不小于30kb
      //cacheGroups会继承这个值
      minSize:30000,
      cacheGroups:{
        //vue相关框架
        main:{
          test: /[\\/]node_modules[\\/]vue[\\/]/,
          name: './js/main',
          chunks:'all'
        },
        //除Vue之外其他框架
        vendors:{
          test:/[\\/]node_modules[\\/]?!(vue)[\\/]/,
          name: './js/vendors',
          chunks:'all'
        },
        //业务中可复用的js
        extractedJS:{
          test:/[\\/]src[\\/].+\.js$/,
          name:'./js/extractedJS',
          chunks:'all'
        }

      }
    }
  }
};

runtimeChunk包含了一些webapck的样板文件,使得你在不改变源文件内容的情况下打包,哈希值仍然改变,因此我们把他单独提取出来,点这儿了解更多。 cacheGroups用于提取复用的模块,test会尝试匹配( 模块的绝对路径||模块名 ),返回值为true且满足条件的模块会被分割。满足的条件可自定义,比如模块最小应该多大尺寸、至少被导入进多少个chunk(即复用的次数)等。默认在打包前模块不小于30kb才被会分割。

4.树抖动

在package.json里加入

"sideEffects":["*.css","*.less","*.sass"]

该数组之外的文件将会受到树抖动的影响——未使用的代码将会从export导出对象中剔除。这将大大减少无用代码。如果树抖动对某些文件具有副作用,就把这些文件名放进数组以跳过此操作。css文件(包括.less,.sass)都必须放进来,否则会出现样式丢失。

5. 插件的使用

5.1 clean-webpack-plugin

每次打包后都会生成新的文件,这可能会导致无用的旧文件堆积,对于这些无用文件自己一个个删太麻烦,这个插件会在每次打包前自动清理。实际中,我们不想在开发环境下清理掉build命令生成的文件,因此只在生产环境使用了这个插件。

5.2 html-Webpack-plugin

我们的源码目录中并没有html文件,打包后的多个html文件,就是我们用这个插件生成的。

//webpack.common.js
// ...省略上面已经出现过的内容
//每个html需要一个插件实例
//批量生成html文件
const createPluginInstance = (list = []) => (
  list.map((item) => {
    return new HtmlWebpackPlugin({
      filename: item.filename ? `${item.filename}.html` : `${item}.html`,
      template: item.template ? `./public/${item.template}` :       './public/template.html',
      title: item.title ? item.title : item,
      chunks: [
        `./js/${item.filename ? item.filename : item}`,
        './js/extractedJS',
        './js/vendors',
        './js/main',
        './js/runtime',
        './css/styles.css',
        devMode ? './css/[id].css' : './css/[id].[contenthash].css',
      ],
    });
  })
);

默认会将所有的入口文件,代码分割后的文件打包进一个html文件里,通过指定 chunks 属性来告诉插件 只包含 哪些块,或者exludeChunks指定不应包含那些chunks。这里有个小问题,我们无法让文件刚好只包含他需要的块。若想不包含未使用的chunks,只能根据实际情况手动配置,用这个函数批量生成的文件,总会包含所有的公共打包文件。

5.3 mini-css-extract-plugin (prooduction)

该插件用于提取js文件中的css到单独的css文件中。

//webpack.prod.js
//...省略其他内容
plugins:[
    new CleanWebpackPlugin('build'),
    // 提取css
    new MiniCssExtractPlugin({
      filename:'./css/[id].[contenthash].css'
    }),
    //优化缓存
    new webpack.HashedModuleIdsPlugin()
  ] 

5.4 optimize-css-assets-webpack-plugin (production)

用于精简打包后的css代码,设置在配置optimization的minimizer属性中,这将会覆盖webpack默认设置,因此也要同时设置js的精简工具(这里我们用uglifyplugin插件):

optimization: {
    minimizer:[
     new UglifyJsPlugin({
      cache: true,
      parallel: true
     }),
     new OptimizeCSSAssetsPlugin()
    ]
  }

6.开发服务器、热模块替换 (development)

webpack.dev.js中增加如下内容即可:

//...省略其他内容
devServer:{
    index:'index.html',
    hot:true,
    contentBase:path.resolve(__dirname,'./build'),
    port:3000,
    noInfo:true
  },
plugins:[
    new webpack.HotModuleReplacementPlugin()
  ]

使用开发服务器可以在我们修改了源文件后自动刷新,因为是将数据放在内存中,因此不会影响硬盘中build文件夹。热模块替换还需要在源文件做相应修改。我们也为动态导入语法进行了相应配置。

7.其他

public用于存放静态资源,打包后也会在build/下创建一个同名文件夹,里面存放的是public会被使用到的资源。如果在.css文件里引用了public里的资源,如图片,添加url的时候要使用绝对路径:

<!-- src/css/page1.css -->
.bg-img {
  background-image:url(/public/images/1.jpg)
}

这样通过 http/https 打开的时候就能正常使用,如果是以文件形式打开(比如打包后双击page1.html),会发现浏览器显示无法找到资源。通过导入图片作为变量引用( import name from path ),既可使用绝对路径,也可使用相对路径。

总结

以上所述是小编给大家介绍的webpack4打包vue前端多页面项目,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 详解webpack 多页面/入口支持&公共组件单独打包

    webpack系列目录 webpack 系列 二:webpack 介绍&安装 webpack 系列 三:webpack 如何集成第三方js库 webpack 系列 四:webpack 多页面支持 & 公共组件单独打包 webpack 系列 五:webpack Loaders 模块加载器 webpack 系列 六:前端项目模板-webpack+gulp实现自动构建部署 基于webpack搭建纯静态页面型前端工程解决方案模板, 最终形态源码见github: https://github.com

  • webpack 3.X学习之多页面打包的方法

    简介 我们开发不可能只写一个页面,每次都要写很多页面,这时为了开发效率,我们使用前端自动化工具webpack,那么webpack是如何打包页面的呢?又是如何打包多页面的呢? 单页面打包 我们知道要打包单页面的方法,很简单,配置入口,和html插件, const HtmlWebpackPlugin = require('html-webpack-plugin'); const config = { entry:{ index:'./src/index.js' }, output:{ path: p

  • webpack打包多页面的方法

    前言 一开始接触webpack是因为使用Vue的关系,因为Vue的脚手架就是使用webpack构建的.刚开始的时候觉得webpack就是为了打包单页面而生的,后来想想,这么好的打包方案,只在单页面上使用是否太浪费资源了呢?如果能在传统多页面上使用webpack,开始效率是否会事半功倍呢?好在众多优秀的前端开发者已经写了许多demo和文章供人学习.我也写了一个小项目,希望对大家学习webpack有帮助. 好吧其实上面说的都是废话,接下来附上项目地址和干货,配合食用更佳. webpack-multi

  • webpack4打包vue前端多页面项目

    之前一直用的脚手架,这次自己搭建webpack前端项目,花费了不少心思,于是做个总结. 1.用法 项目结构如下: project |- bulid <!-- 这个目录是自动生成的--> |- public |- css |- js |- page1.html <!-- 插件生成的html文件--> |- page2.html <!-- 插件生成的html文件--> ... |- public/ <!-- 存放字体.图片.网页模板等静态资源--> |- src

  • 详解webpack4多入口、多页面项目构建案例

    趁工作之余从零构建了一个webpack4.x多页面应用程序.过程中也遇到一些坑,就记录下来了. webpack核心概念 Entry:入口,Webpack 执行构建的第一步将从 Entry 开始. Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件.Webpack 会从配置的 Entry 开始递归找出所有依赖的模块. Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割. Loader:模块转换器,用于把模块原内容按照需求转换成新内容. Plugi

  • vue前端项目打包成Docker镜像并运行的实现

    目录 vue前端项目打包成Docker镜像并运行 前端将vue打包成镜像发布 一.总体预览 二.打包配置 三.问题思考 vue前端项目打包成Docker镜像并运行 首先说明咱们的前端项目是基于Vue的,反向代理使用的是nginx 1.打包vue前端项目生成dist文件夹上传至服务器 新建一个文件夹,叫vueDockerTest,下面的文件都需要. cert是你存放ssl证书的文件夹,nginx.conf 是nginx的配置文件,dist是你打包的前端静态文件 2.修改Dockerfile文件 #

  • Vue项目优化打包之前端必备加分项

    目录 前言 一.路由懒加载 1. 为什么需要路由懒加载 2. 如何实现路由懒加载 3. 路由懒加载中的魔法注释 二.分析包大小 1. 需求 2. 如何生成打包分析文件 三.webpack配置排除打包 1. 需求 2. 排除打包 四. 引用网络资源 1. 需求 2. CDN 3. 实现步骤 五. 打包去除console.log 1. 需求 2. 代码演示 总结 前言 Vue项目开发完毕后,对项目进行打包发布之前,必不可少的操作就是项目优化,这也是程序猿的加分项.跟随本文的脚步来看看如何对项目进行优

  • vue实现的多页面项目如何优化打包的步骤详解

    遇到的问题 在多页面框架打包的过程中会,随着业务的增加页面越来越多,使用的三方包也会越来越多,但并不是所有页面都会使用到三方插件,使用webpack打包会让所有的三方包打包到一起,会导致vendor.js(三方包打包后的文件)越来越大,即使没使用过三方插件的页面也会引入,页面加载会越来越慢. 如何优化 使用cdn引入,这种就每次新建一个页面的时候手动通过cdn的方式引入,但是并不是特别好的方式,还需要手动加入. 就是使用splitChunks分割三方包,将三方包单独打包出来,根据页面的依赖情况自

  • 教你用Cordova打包Vue项目的方法

    现在国内越来越多的开发者使用Vue开发混合app,但是当大家开发完成过后才发现不知道该怎么将Vue项目打包成app. 据我现在的了解打包Vue项目目前流行的就是使用weex和cordova.weex是阿里提供并且Vue的作者也极力推荐的,有兴趣的可以去学习使用一下.因为我本身是做angular+ionic的,所以比较青睐cordova,下面我就教大家怎么使用cordova打包Vue项目: 第一步:安装cordova 如果已经安装则直接跳过,否则执行以下命令: npm install -g cor

  • vue cli构建的项目中请求代理与项目打包问题

    在上篇文章给大家介绍了vue-cli webpack模板项目搭建及打包时路径问题的解决方法,可以点击查看. vue-cli构建的项目中,生产模式下的打包路径.与生产模式下的请求代理简单示意 总结 以上所述是小编给大家介绍的vue cli构建的项目中请求代理与项目打包问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的.在此也非常感谢大家对我们网站的支持! 您可能感兴趣的文章: Vue-cli创建项目从单页面到多页面的方法 Vue使用vue-cli创建项目 vue-cli项

  • vue、react等单页面项目部署到服务器的方法及vue和react的区别

    最近好多伙伴说,我用vue做的项目本地是可以的,但部署到服务器遇到好多问题:资源找不到,直接访问index.html页面空白,刷新当前路由404...用react做的项目也同样遇到类似问题.现在我们一起讨论下单页面如何部署到服务器? 由于前端路由缘故,单页面应用应该放到nginx或者apache.tomcat等web代理服务器中,千万不要直接访问index.html,同时要根据自己服务器的项目路径更改react或vue的路由地址. 如果说项目是直接跟在域名后面的,比如:http://www.so

  • 解决vue 打包发布去#和页面空白的问题

    1.vue项目中config文件下index.js中打包配置 build: { // Template for index.html index: path.resolve(__dirname, '../yiTownWebApp/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../yiTownWebApp'), assetsSubDirectory: 'static', assetsPublicPath: '/yiTown

  • SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建过程(前端篇)

    后端篇 SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建[后端篇][快速生成后端代码.封装结果集.增删改查.模糊查找][毕设基础框架] 前端篇 创建vue项目 1.找个文件夹进入命令行,输入:vue create vue-front 2.直接回车,等待片刻,稍微有点小久 3.根据提示指令测试 打开浏览器输入:http://localhost:8080/ 安装所需工具 安装的工具会有点多,为了提供更好的拓展性,可以自主选择安装(不建议),后面的代码中都是使用到了,不安装

随机推荐