不借助脚手架使用webpack4从0开始搭建一个完整vue开发环境

目录

一、安装webpack和webpack-cli

二、指令方式运行

三、配置文件方式运行

四、less-loader解析less

 五、一般es6语法转换

 六、JS兼容性处理(包括ie)

七、使用url-loader解析样式中的图片

 八、打包html中图片资源

九、打包html文件

十、打包字体等资源

十一、自动编译打包运行

十二、多页面配置打包

十三:ESLint 校验 JS 代码

十四、提取css成单独文件

 十五、添加css兼容

十六、打包 Ts 和 Scss

十七、压缩js、css和html

十八、Resolve 模块解析

十九、Source-Map 配置(devtool映射)

二十、watch 监听和 clean 清理

二十一、HMR 热替换

二十二、请求代理设置

二十三、支持vue单文件组件

 二十四:环境分离设置

二十五、webpack中path、outputPath、publicPath联系与区别

二十六、总结

二十七、项目完整代码


一、安装webpack和webpack-cli

npm i [email protected] [email protected] -g //全局安装,作为指令使用
npm i [email protected] [email protected] -D //本地安装,作为本地依赖使用

注意:如果是局部安装,则需要在package.json中配置scripts,通过运行"npm run start"或"npm start"运行webpack:

{
  "scripts": {
    "start": "webpack"
  }
}

二、指令方式运行

开发配置指令:

webpack src/js/index.js -o dist/js/index.js --mode=development

生产配置指令:

webpack src/js/index.js -o dist/js/index.js --mode=production

可在package.json中配置scripts,通过运行"npm run build"运行webpack:

 "scripts": {
    "build": "webpack src/js/index.js -o dist/js/index.js --mode=production"
  }

三、配置文件方式运行

webpack.config.js:

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */

const path = require('path');

module.exports = {
  entry: './src/js/index.js', // 由cmd执行目录决定
  output: {
    path: path.resolve(__dirname, 'dist/js'), // 由当前文件所在位置和要生成的dist位置关系决定
    filename: 'bundle.js'
  },
  mode: 'development',
  devtool: 'inline-source-map'
};

配置好上述文件后在cmd中通过:

webpack

命令即可运行

假如webpack配置文件为“./config/webpack.base.config.js”,则package.json中打包命令配置为:

  "scripts": {
    "build": "webpack --config ./config/webpack.base.config.js"
  }

四、less-loader解析less

概述:webpack不能直接解析less文件,需要借助loader编译解析。

1、安装loader:

npm i [email protected] [email protected] [email protected] [email protected] -D

less-loader基于less,所以要安装less。

注意各包的版本,否则会报错。

package.json:

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^4.3.0",
    "less": "^3.12.2",
    "less-loader": "^7.0.1",
    "style-loader": "^2.0.0",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}

2、配置loader:

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */

const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist/js'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: 'bundle.js'
    },
    mode: 'production',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }]
    }
};

3、运行指令:webpack

附:

src/css/demo.less:

.demo1{
    width: 500px;
    height: 500px;
    background: #FF0000;
}
.demo2{
    width: 200px;
    height: 200px;
    background: #000000;
}

src/js/index.js: 

import func1 from './moudle1'
import func2 from './moudle2'
import '../css/demo.less'

console.log(func1(1, 2));
console.log(func2(2, 3));
console.log("webpack!");

index.html:




    
    
    
    Document


    

 五、一般es6语法转换

高版本的火狐、chrome等标准浏览器直接支持es6中const和箭头函数等一般语法及promise等高级语法,但低版本的火狐、chrome等标准浏览器及ie不行,使用babel-loader可以将一般es6语法转换成es5语法。

安装loader:

npm i [email protected] @babel/[email protected] @babel/[email protected] -D

babel-loader:与 Webpack 协同工作的模块,加载处理 js 文件;

@babel/core:Babel 编译器的核心模块,是 babel-loader 依赖;

@babel/preset-env:Babel 预置器,用于分析 ES6 语法;

配置loader:

module: {
        rules: [{
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env']
              }
            }
        }]
}

我们在 src 中的 js 文件,使用 ES6 的箭头函数来尝试一下;

let fn = (x, y) => x + y;
console.log(fn(10, 20));

PS:在没有使用 Babel 时,它打包会原封不动的使用 ES6 语法;

PS:在使用 Babel 之后,编译后的代码如下:

var fn = function fn(x, y) {
    return x + y;
};

console.log(fn(10, 20));

如果你使用了未纳入标准(提案中)的代码,打包时,它会提醒你安装相关插件:

//提案中,尚未纳入标准的语法
class Person {
    #name;
    constructor() {
        this.#name = 'Mr.Lee';
    }
}

 安装相关插件:

npm i @babel/plugin-proposal-class-properties -D
options: {
    presets : [
        '@babel/preset-env'
    ],
    plugins : [
        '@babel/plugin-proposal-class-properties'
    ]
}

 六、JS兼容性处理(包括ie)

高版本的火狐、chrome等标准浏览器直接支持es6中const和箭头函数等一般语法及promise等高级语法,但低版本的火狐、chrome等标准浏览器及ie不行,使用polyfill或core.js可以将所有es6语法转换成es5语法,使得其兼容性更强(包括兼容ie)。

1、使用polyfill

安装polyfill:

cnpm i @babel/[email protected] -D

使用:

入口文件中直接引入:

import '@babel/polyfill'
/**
 * 当前文件是入口文件,可汇总js、css、字体、图片、音频、视频
 */
import '@babel/polyfill' //包括es6高级语法的转换,不管编码人员使用了哪些新语法,全部新语法都转换
import module1 from './module1'
import module2 from './module2'
import module3 from './module3'
import '../css/demo.less'

module2().then(data => {
    console.log("promise resolve:" + data);
}).catch(error => {
    console.log("promise reject:" + error);
})

console.log(module2);
console.log(module3);

setTimeout(function(){
    console.log("定时器到点了");
}, 1000)

优点:解决babel只能转换部分低级语法的问题,引入polyfill后可转换高级语法,比如Promise。

缺点:将所有高级语法进行转换,不管实际使用情况。

2、借助core.js按需引入(babel+core.js,使用core.js时可以不用polyfill):

安装core-js:

npm i [email protected] -D

使用core-js:

配置文件中loader方式引入

module: {
        rules: [{
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    "presets": [
                        ["@babel/preset-env", {
                            useBuiltIns: "usage", //使用corejs的方式,表示按需加载
                            corejs: { version: 3 }, //指定core-js的版本
                            targets: { //要兼容的目标浏览器
                                "chrome": "58",
                                "ie": "9"
                            }
                        }]
                    ],
                    "cacheDirectory": false // 开启babel缓存,只编译未编译代码,可减少编译时间
                }
            }
        }]
}

七、使用url-loader解析样式中的图片

图片文件分为css引入图片和html插入图片。css加载图片都是背景图。小图片采用base64转换字符串,小图片、大图片都需要单独的loader插件来完成。

用到的处理图片的loader插件如下:

  • file-loader:解析JavaScipt和css插入的图片;
  • url-loader:将图片转换为性价比最高的base64编码字符串(基于file-loader,file-loader有的功能它都有);
  • html-loader:将.html进行打包,从而解析img插入问题;

安装loader:

npm i [email protected] [email protected] -D

说明:url-loader基于file-loader,且多了能针对不同图片大小进行不同处理的功能。

配置loader:

module: {
        rules: [{
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader', //url-loader依赖file-loader,使用了url-loader后file-loader可以不用
                    options: {
                        limit: 8192, //url-loader能将小于8k图片编译成base64,大的采用file-loader处理
                        publicPath: './dist/images', //决定实际代码中的图片地址
                        outputPath: 'images', //决定文件本地输出地址
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }]
    }

一定要注意webpack.config.js中publicPath路径,包含图片的样式打包后会放置在页面的style标签中,图片打包后会放置在dist/images目录下,根据页面和生成的图片的路径关系,所以publicPath为 './dist/images':

不借助脚手架使用webpack4从0开始搭建一个完整vue开发环境_第1张图片

webpack.config.js: 

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */

const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js'
    },
    mode: 'development',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }, {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }, {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    "presets": [
                        ["@babel/preset-env", {
                            useBuiltIns: "usage", //按需引入需要使用polyfill
                            corejs: { version: 3 }, //解决不能找到core-js的问题
                            targets: { //解决兼容性处理哪些浏览器
                                "chrome": "58",
                                "ie": "9"
                            }
                        }]
                    ],
                    "cacheDirectory": false // 开启babel缓存
                }
            }
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        publicPath: './dist/images', //决定图片的url路径
                        outputPath: 'images', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }]
    }
};

各loader版本:

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "babel-loader": "^8.1.0",
    "css-loader": "^4.3.0",
    "file-loader": "^6.2.0",
    "less": "^3.12.2",
    "less-loader": "^7.0.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}

index.js

import func1 from './moudle1'
import func2 from './moudle2'
import '../css/demo.less'
 
console.log(func1(1, 2));
console.log(func2(2, 3));
console.log("webpack!");

 moudle1.js

const fun1 = (a, b) => {
    console.log("a + b = ", a+b);
}

module.exports = fun1;

moudle2.js

const fun2 = (a, b) => {
    console.log("a + b = ", a+b);
}

module.exports = fun2;

 demo.less

.demo1{
    width: 500px;
    height: 500px;
    background: #FF0000 url("../images/download.png");
}
.demo2{
    width: 200px;
    height: 200px;
    background: #000000;
}

index.html





    
    
    
    Document



    

 八、打包html中图片资源

概述:html中的图片url-loader没法处理,它只能处理js中引入的图片/样式中的图片,不能处理html中的img标签,需要引入html-loader处理。

添加图片:在src/index.html添加两个img标签。

安装loader:

npm i [email protected] -D

配置loader:

module: {
        rules: [{
            test: /\.(html)$/,
            use: {
                loader: 'html-loader' // html-loader找到图片后自动交给url-loader处理
            }
        }]
    }

完整代码:

不借助脚手架使用webpack4从0开始搭建一个完整vue开发环境_第2张图片

webpack.config.js:

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js'
    },
    mode: 'development',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }, {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        publicPath: './images', //决定图片的url路径
                        outputPath: 'images', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }, {
            test: /\.(html)$/,
            use: {
                loader: 'html-loader'
            }
        }]
    },
    plugins: [new HtmlWebpackPlugin({
       template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
       filename: "index.html", // 打包后的index.html文件默认位于dist目录下
    })]
};

一定要注意url-loader中的publicPath。

index.html:




    
    
    
    Document


demo.less:

.demo1{
    width: 200px;
    height: 200px;
    background-image: url("../images/react.png");
    background-size: cover;
}
.demo2{
    width: 200px;
    height: 200px;
    background-image: url("../images/vue.jpg");
    background-size: cover;
}

package.json:

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "babel-loader": "^8.1.0",
    "css-loader": "^4.3.0",
    "file-loader": "^6.2.0",
    "html-loader": "^1.1.0",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.12.2",
    "less-loader": "^7.0.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  },
  "dependencies": {}
}

九、打包html文件

概述:html文件webpack不能解析,需要借助插件编译解析。

添加html文件:

  1. src/index.html
  2. 注意:不要在html中引入任何css和js文件

安装插件Plugins

npm i [email protected] -D

在webpack.config.js中引入插件(插件都需手动引入,而loader会自动加载):

const HtmlWebpackPlugin = require('html-webpack-plugin');

配置插件:

    plugins: [new HtmlWebpackPlugin({
        template: './src/index.html', //以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认。路径相对于npm命令执行所在目录。
        filename: 'index.html' // 打包后的index.html文件默认位于dist目录下
    })]

运行指令:

webpack

完整代码:

src/index.html:




    
    
    
    Document


    

src/js/index.js:

import func1 from './moudle1'
import func2 from './moudle2'
import '../css/demo.less'

const word = "webpack!";
console.log("webpack!");
console.log(func1(1, 2));
func2().then(data=>{
    console.log(data);
}).catch(error=>{
    console.log(error);
})

删除

import '@babel/polyfill'

 webpack.config.js: 

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js'
    },
    mode: 'development',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }, {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }, {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    "presets": [
                        ["@babel/preset-env", {
                            useBuiltIns: "usage", //按需引入需要使用polyfill
                            corejs: { version: 3 }, //解决不能找到core-js的问题
                            targets: { //解决兼容性处理哪些浏览器
                                "chrome": "58",
                                "ie": "9"
                            }
                        }]
                    ],
                    "cacheDirectory": false // 开启babel缓存
                }
            }
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        publicPath: './images', //决定图片的url路径
                        outputPath: 'images', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }]
    },
    plugins: [new HtmlWebpackPlugin({
       template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
       filename: "index.html", // 打包后的index.html文件默认位于dist目录下
    })]
};

十、打包字体等资源

概述:其它资源,比如图标,webpack不能解析,需要借助loader编译解析。

安装loader:

npm i [email protected] -D

配置loader:

{
            test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4|avi)$/,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        outputPath: 'media', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }

src/index.html中添加字体标签:





    
    
    
    Document



    
    
    
    
    
    
    

添加字体样式文件src/css/iconfont.less:

@font-face {
  font-family: "iconfont"; /* Project id  */
  src: url('../media/iconfont.ttf?t=1621867822279') format('truetype');
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-caiwuguanli:before {
  content: "\e609";
}

.icon-baojiaguanli:before {
  content: "\e60a";
}

.icon-baoguanzhidantaitouguanli:before {
  content: "\e60b";
}

.icon-caigoudingdan:before {
  content: "\e60c";
}

.icon-cangchuguanli:before {
  content: "\e60d";
}

.icon-caigoulujing:before {
  content: "\e60e";
}

index.js中引入iconfont.less文件:

import func1 from './moudle1'
import func2 from './moudle2'
import '../css/demo.less'
import '../css/iconfont.less'

const word = "webpack!";
console.log("webpack!");
console.log(func1(1, 2));
func2().then(data=>{
    console.log(data);
}).catch(error=>{
    console.log(error);
})

运行指令:

webpack

文件夹结构:

不借助脚手架使用webpack4从0开始搭建一个完整vue开发环境_第3张图片

iconfont.less(注意引用的字体路径):

@font-face {
  font-family: "iconfont"; /* Project id  */
  src: url('../media/iconfont.ttf?t=1621867822279') format('truetype');
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-caiwuguanli:before {
  content: "\e609";
}

.icon-baojiaguanli:before {
  content: "\e60a";
}

.icon-baoguanzhidantaitouguanli:before {
  content: "\e60b";
}

.icon-caigoudingdan:before {
  content: "\e60c";
}

.icon-cangchuguanli:before {
  content: "\e60d";
}

.icon-caigoulujing:before {
  content: "\e60e";
}

index.html:




    
    
    
    Document


        
        
        
        
        
        

webpack.config.js:

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js'
    },
    mode: 'development',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }, {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        //publicPath: '../dist/images', //决定图片的url路径
                        outputPath: 'images', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }, {
            test: /\.(html)$/,
            use: {
                loader: 'html-loader'
            }
        }, {
            test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4|avi)$/,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        outputPath: 'media', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }]
    },
    plugins: [new HtmlWebpackPlugin({
      template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
      filename: "index.html", // 打包后的index.html文件默认位于dist目录下
    })]
};

package.json:

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "babel-loader": "^8.1.0",
    "css-loader": "^4.3.0",
    "file-loader": "^6.2.0",
    "html-loader": "^1.1.0",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.12.2",
    "less-loader": "^7.0.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  },
  "dependencies": {}
}

十一、自动编译打包运行

由于代码可能有CommonJS等非浏览器支持的语法,每次都必须打包才行运行,虽然借助Webstorm等工具可以构建服务器环境,但实际上不能时时监控刷新。IDE提供的服务器之间访问的是打包后的文件,是否监听时时刷新看个人习惯。

以上:如果要方便的话,开发者需求的想法是,开发时方便调试,最后再打包。所以,官方提供了Webpack-dev-Server工具来解决这个问题,支持特性:

  • 支持HTTP服务访问:localhost,127.0.0.1这种;
  • 监听变化时时刷新网页,时时预览;
  • 支持SourceMap;

注:

此处是自动刷新整个页面 ,如果想做到局部刷新,则请浏览:二十二、HMR 热替换

1、安装loader:

npm i [email protected] -D

详情见:官网->指南->开发环境->使用webpack-dev-server

2、修改webpack配置对象:

devServer: {
        open: true,
        compress: true,
        port: 3000,
        stats: 'minimal'//迷你型服务启动信息
}

webpack.config.js: 

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js' // 直接在根目录下生成js文件夹及bundle.js文件
    },
    mode: 'development',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }, {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        //publicPath: '../dist/images', //决定图片的url路径
                        outputPath: 'images', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }, {
            test: /\.(html)$/,
            use: {
                loader: 'html-loader'
            }
        }, {
            test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4|avi)$/,
            use: [
                {
                    loader: 'file-loader',
                    options: {
                        outputPath: 'media', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }]
    },
    plugins: [new HtmlWebpackPlugin({
      template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
      filename: "index.html", // 打包后的index.html文件默认位于dist目录下
    })],
    devServer: {
        open: true,
        compress: true,
        port: 8080,
        stats: 'minimal',//迷你型服务启动信息
    },
};

3、修改url-loader部分配置:

  • 因为构建工具以build为根目录,不用再找build了
  • publicPath: '../build/images'-> 'images'

4、修改package.json中scripts指令:

"start": "webpack-dev-server --open chrome --port 3000"

 5、./index.html:





    
    
    
    Document



    1
    

6、运行指令:

npm start

经过以上设置,处理的html是根目录下的index.html,打包后的js位于设置的根目录下的js文件夹下。 

本地我们可以删除dist目录,还原打包之前再启动devServer测试效果。此时我们可以发现并不需要打包到本地,它是自动打包到内存让你时时预览调试的。也就是说:调试阶段,可以用devServer,完成了,再最终打包到本地即可。

十二、多页面配置打包

多页面配置

如果我们想生成多个.html 文件,比如 index.html 和 main.html,此时,我们需要修改一下入口文件和出口文件:

//入口文件
entry: {
    //把需要加载的 js 以键值对方
    index : './src/js/index.js',
    main : './src/js/main.js'
},

//入口文件,也支持 ES6 的箭头函数
entry: () => ({
    index : './src/js/index.js',
    main : './src/js/main.js'
}),

 出口文件,需要按照入口文件的名称进行打包,否则只会打包一个。

//出口文件
output: {
    //文件名
    filename : 'js/[name].js',
}),

最后,我们要使用 HtmlWebpackPlugin 插件来设置自己想要的打包方案:

//插件
plugins: [
    new HtmlWebpackPlugin({
        template: "./src/index.html", //默认值
        filename: "index.html", //默认值
        chunks: ['index', 'main']
    }),
    new HtmlWebpackPlugin({
        template: "./src/main.html",
        filename: "main.html",
        chunks: ['main']
    }),
],

十三:ESLint 校验 JS 代码

基本的 ESLint 实现,需要一下安装以下模块:

  • eslint:JS 代码检查工具核心模块;
  • eslint-loader:webpack 协同模块;

首先,先安装 eslint,然后安装配置信息:

npm i [email protected] -D //安装 eslint

.\node_modules\.bin\eslint --init //安装配置信息

PS:期间会让你选择配置信息情况,根据你实际情况选择即可,生成:.eslintrc.json;
PS:网上也会别人生成的配置信息可以拿来用,也可以去官网 eslint.cn/demo 生成信息

再次,我们安装 eslint-loader 模块:

npm i [email protected] -D

最后,我们来配置 webpack.config.js:

{
    test : /.js$/,
    loader: 'eslint-loader',
    //编译前执行
    enforce: 'pre',
    //不检查的目录
    exclude: /node_modules/
},

防止错误飘红太多,干扰演示,我们注释掉大部分代码,写上示例:

var foo = bar;

 测试打包和预览提示校验。

注意插件和loader的版本:

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config ./config/webpack.prod.config.js",
    "dev": "webpack-dev-server --open chrome --port 8080 --config ./config/webpack.dev.config.js",
    "start": "webpack-dev-server --open chrome --port 8080 --config ./config/webpack.dev.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/polyfill": "^7.12.1",
    "@babel/preset-env": "^7.11.5",
    "autoprefixer": "^9.8.6",
    "axios": "^0.22.0",
    "babel-loader": "^8.1.0",
    "clean-webpack-plugin": "^4.0.0",
    "core-js": "^3.13.0",
    "css-loader": "^4.3.0",
    "eslint": "^7.2.0",
    "eslint-loader": "^4.0.2",
    "file-loader": "^6.2.0",
    "html-loader": "^1.1.0",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.12.2",
    "less-loader": "^7.0.1",
    "mini-css-extract-plugin": "^1.6.0",
    "optimize-css-assets-webpack-plugin": "^6.0.0",
    "postcss-flexbugs-fixes": "^4.2.1",
    "postcss-loader": "^3.0.0",
    "postcss-normalize": "^8.0.1",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "vue-loader": "^15.9.8",
    "vue-template-compiler": "^2.6.14",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.11.2",
    "webpack-merge": "^5.8.0"
  },
  "dependencies": {
    "vue": "^2.6.14"
  }
}

十四、提取css成单独文件

  • 安装插件:
cnpm i [email protected] -D
  • 引入插件:
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
  • 配置插件:
new MiniCssExtractPlugin({
        filename: 'css/[name].css'
    })
  • 配置loader:
{
                test: /\.less$/, //匹配所有less文件
                use: [
                        {
                            loader: MiniCssExtractPlugin.loader,
                            options: {
                                publicPath: '../'
                            }
                        },
                        'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                        'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
                ]
            }
  • 运行指令:

package.json

  "scripts": {
    "build": "webpack",
    "build": "webpack --config ./config/webpack.prod.js",
    "dev": "webpack-dev-server --open chrome --port 8080 --config ./config/webpack.dev.js"
  },
npm run dev

完整代码:

src/index.html:





    
    
    
    Document



    
    
    
    
    
    
    

config/webpack.dev.js:

const path = require('path')
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                           publicPath: '../'
                        }
                    },
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.m?js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                useBuiltIns: "usage", //按需引入需要使用polyfill
                                corejs: { version: 3 }, //解决不能找到core-js的问题
                                targets: { //解决兼容性处理哪些浏览器
                                    "chrome": "58",
                                    "ie": "9"
                                }
                            }]
                        ],
                        "cacheDirectory": false // 开启babel缓存,只编译未编译代码,可减少编译时间
                    }
                }
            },
            {
                test: /\.(png|jpg|gif|jpeg)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        //publicPath: '../images',
                        outputPath: 'images',
                        name: '[hash:5].[ext]'
                    }
                }
            },
            {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader'
                }
            },
            {
                test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        //publicPath: '../media',
                        outputPath: 'media',
                        name: '[hash:5].[ext]'
                    }
                }]
            }
        ]
    },
    plugins: [new HtmlWebpackPlugin({
        template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
        filename: "index.html", // 打包后的index.html文件默认位于dist目录下
        minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
        }
    }), new MiniCssExtractPlugin({
        filename: './css/[name].css'
    })],
    devServer: {
        open: true,
        compress: true,
        port: 8080,
        hot: true
    }
}

一定要注意:这里使用url-loader生成的背景图片是在css文件中引入,所有这里查找图片是从build/css目录去build/images目录查找:

{
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192, //url-loader能将小于8k图片编译成base64,file-loader不具此功能
                            //publicPath: '../images', //决定图片的url路径
                            outputPath: 'images', //决定文件本地输出路径
                            name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                        }
                    }
                ]
            }

config/webpack.prod.js: 

const path = require('path')
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    entry: './src/js/index.js',
    output: {
        path: path.resolve(__dirname, '../dist'),
        filename: './js/bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    {
                            loader: MiniCssExtractPlugin.loader,
                            options: {
                                publicPath: '../'
                            }
                    },
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.m?js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                useBuiltIns: "usage", //按需引入需要使用polyfill
                                corejs: { version: 3 }, //解决不能找到core-js的问题
                                targets: { //解决兼容性处理哪些浏览器
                                    "chrome": "58",
                                    "ie": "9"
                                }
                            }]
                        ],
                        "cacheDirectory": false // 开启babel缓存,只编译未编译代码,可减少编译时间
                    }
                }
            },
            {
                test: /\.(png|jpg|gif|jpeg)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        //publicPath: '../images',
                        outputPath: 'images',
                        name: '[hash:5].[ext]'
                    }
                }
            },
            {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader'
                }
            },
            {
                test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        //publicPath: '../media',
                        outputPath: 'media',
                        name: '[hash:5].[ext]'
                    }
                }]
            }
        ]
    },
    plugins: [new HtmlWebpackPlugin({
        template: './src/index.html',
        minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
        }
    }), new MiniCssExtractPlugin({
        filename: './css/[name].css'
    })]
}

 十五、添加css兼容

方法一:

  • 安装loader:
cnpm i [email protected] [email protected] [email protected] [email protected] [email protected] -D
  • 配置loader:
{
                        loader: require.resolve('postcss-loader'),
                        options: {
                          ident: 'postcss',
                          plugins: () => [
                            require('postcss-flexbugs-fixes'),
                            require('postcss-preset-env')({
                              autoprefixer: {
                                flexbox: 'no-2009',
                              },
                              stage: 3,
                            }),
                            require('postcss-normalize')()
                          ],
                          sourceMap: true,
                        },
}
  • webpack.prod.js:
/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, '../build'), // 由当前文件所在位置和要生成的build位置关系决定
        filename: './js/bundle.js',
        publicPath: '/'
    },
    mode: 'production',
    devtool: 'cheap-module-source-map',
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                useBuiltIns: "usage", //按需引入需要使用polyfill
                                corejs: { version: 3 }, //解决不能找到core-js的问题
                                targets: { //解决兼容性处理哪些浏览器
                                    "chrome": "58",
                                    "ie": "9"
                                }
                            }]
                        ],
                        "cacheDirectory": false // 开启babel缓存,只编译未编译代码,可减少编译时间
                    }
                }
            },
            {
                test: /\.less$/, //匹配所有less文件
                use: [
                    MiniCssExtractPlugin.loader, //3、用于在html文档中创建一个style标签,将样式“塞”进去
                    'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                    {
                        loader: require.resolve('postcss-loader'),
                        options: {
                          ident: 'postcss',
                          plugins: () => [
                            require('postcss-flexbugs-fixes'),
                            require('postcss-preset-env')({
                              autoprefixer: {
                                flexbox: 'no-2009',
                              },
                              stage: 3,
                            }),
                            require('postcss-normalize')()
                          ],
                          sourceMap: true,
                        },
                    },
                    'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
                ]
            }, {
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192, //url-loader能将小于8k图片编译成base64,file-loader不具此功能
                            //publicPath: '../images', //决定图片的url路径
                            outputPath: 'images', //决定文件本地输出路径
                            name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                        }
                    }
                ]
            }, {
                test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4|avi)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media', //决定文件本地输出路径
                            name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                        }
                    }
                ]
            }, {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader'
                }
            }
        ]
    },
    plugins: [new HtmlWebpackPlugin({
        template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
        filename: "index.html", // 打包后的index.html文件默认位于dist目录下
        minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
        }
    }), new MiniCssExtractPlugin({
        filename: 'css/[name].css'
    }), new OptimizeCSSAssetsPlugin({
        cssProcessorPluginOptions: {
            preset: [
                'default',
                {
                    discardComments: {
                        removeAll: true
                    },
                },
            ]
        },
        cssProcessorOptions: {
            map: {
                inline: false,
                annotation: true
            }
        }
    })]
};
  • 添加.browserslistrc文件:
#Browsers that we support

last 1 version
> 1%
IE 10 # sorry

方法二:

npm i [email protected] -D
  • 将'postcss-loader' 添加在loader最后:
            {
                test: /\.css$/,
                //执行顺序是从右往左边
                use : [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            publicPath : '../'
                        }
                    },
                    {
                        loader: "css-loader",
                        options: {
                            importLoaders : 1 //由于采用了@import,被导入的css无法解析,需要设置importLoaders=1即可
                        }
                    },
                    'postcss-loader'
                ]
            }
  • 单有postcss-loader还不行,需要搭配创建autoprefixer插件转换。
npm i [email protected] -D

这个插件会在需要兼容性的样式中加上CSS前缀,比如:-ms、-webkit等。在根目录创建postcss.config.js用来配置兼容的浏览器,当postcss-loader被调用时postcss.config.js文件就会执行。

postcss.config.js

/*
    postcss配置文件
 */

const AutoPrefixer = require('autoprefixer');

module.exports = {
    plugins : [
        new AutoPrefixer({
            overrideBrowserslist : [
                '> 0.15% in CN'
                //'ie >= 8'
            ]
        })
    ]
};

在index.css中添加css样式:

display: flex;
transform: rotateY(130deg);

编译后看编译完成的css文件中是否有兼容性代码。

  • 除了兼容浏览器之外,还可以通过postcss-preset-env实现CSS4代码兼容。
npm i [email protected] -D

index.css中添加如下样式:

:root{
    --green:green;
};

h1{
    color:var(--green);
}

 postcss.config.js修改如下:

//请注意:如果使用postcss-preset-env,就删除autoprefixer
//因为post-preset-env集成了autoprefixer,不需要再引入设置

// const AutoPrefixer = require('autoprefixer');
const PostPresetEnv = require('postcss-preset-env');

module.exports = {
    plugins : [
        new PostPresetEnv({
            browsers : [
                '> 0.15% in CN'
                //'ie >= 8'
            ]
        })
    ]
};

编译后看编译完成的css文件中是否有兼容性代码。 

十六、打包 Ts 和 Scss

Scss

和 Less 一样,Scss 也是 Sass 的一种使用较多的语法;

cnpm install sass sass-loader node-sass -D

具体配置基本和 Less 一样,先创建.scss 文件,并引入:

{
    test: /\.scss$/,
    //从右向左执行
    use : [
        MiniCssExtractPlugin.loader,
        'css-loader',
        'sass-loader'
    ]
},

 TypeScript

越来越流行的 TypeScript,JavaScript 的一个超集。

cnpm i typescript ts-loader -D
{
    test: /\.ts$/,
    use : 'ts-loader',
}

 编写一个.ts 文件,然后通过 import 导入到 index.js。

//type.ts
export const output = (content : string) => {
    return content
}

//import ts
import { output } from './type.ts'
console.log(output('Mr.Lee'))

如果要省略掉 import 中文件后缀,需要配置:

module.exports = {
    resolve: {
        extensions: ['.ts', '.js']
    }
}

 PS:需要创建 tsconfig.json 文件。

十七、压缩js、css和html

为何要压缩代码?什么情况下要压缩?

答:在生产环境下打包时节约资源。

既然在生产环境,那首先要把打包的配置更改为生产环境:

mode: "production"

调节为生产环境打包,就会自动将 js 代码进行打包,不需要单独设置。

压缩css 

  • 安装插件:
npm i [email protected] -D
  • 引入插件:
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
  • 配置插件:
new OptimizeCSSAssetsPlugin({
        cssProcessorPluginOptions: {
            preset: [
                'default',
                {
                    discardComments: {
                        removeAll: true
                    },
                },
            ]
        },
        cssProcessorOptions: {
            map: { 
                inline: false,
                annotation: true
            }
        }
    })
  • 运行指令:
npm run build
serve -s build

webpack.prod.js:

/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, '../build'), // 由当前文件所在位置和要生成的build位置关系决定
        filename: './js/bundle.js',
        publicPath: '/'
    },
    mode: 'production',
    devtool: 'cheap-module-source-map',
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                useBuiltIns: "usage", //按需引入需要使用polyfill
                                corejs: { version: 3 }, //解决不能找到core-js的问题
                                targets: { //解决兼容性处理哪些浏览器
                                    "chrome": "58",
                                    "ie": "9"
                                }
                            }]
                        ],
                        "cacheDirectory": false // 开启babel缓存,只编译未编译代码,可减少编译时间
                    }
                }
            },
            {
                test: /\.less$/, //匹配所有less文件
                use: [
                    MiniCssExtractPlugin.loader, //3、用于在html文档中创建一个style标签,将样式“塞”进去
                    'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                    'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
                ]
            }, {
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192, //url-loader能将小于8k图片编译成base64,file-loader不具此功能
                            publicPath: '../images', //决定图片的url路径
                            outputPath: 'images', //决定文件本地输出路径
                            name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                        }
                    }
                ]
            }, {
                test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4|avi)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            outputPath: 'media', //决定文件本地输出路径
                            name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                        }
                    }
                ]
            }, {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader'
                }
            }
        ]
    },
    plugins: [new HtmlWebpackPlugin({
      template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
      filename: "index.html", // 打包后的index.html文件默认位于dist目录下
    }), new MiniCssExtractPlugin({
        filename: 'css/[name].css'
    }), new OptimizeCSSAssetsPlugin({
        cssProcessorPluginOptions: {
            preset: [
                'default',
                {
                    discardComments: {
                        removeAll: true
                    },
                },
            ]
        },
        cssProcessorOptions: {
            map: { 
                inline: false,
                annotation: true
            }
        }
    })]
};

压缩html

修改前面安装的html-webpack-plugin插件配置:

new HtmlWebpackPlugin({
        template: './src/index.html', //以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源)
        minify: {
            removeComments: true, // 是否移除注释 默认 false
            collapseWhitespace: true, // 是否去除空格,默认 false
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
        }
    })

十八、Resolve 模块解析

JS中,在 import 文件时如果不限定后缀会出现无法载入的情况,我们可以使用Resolve 模块中的extensions 来进行解析:

//模块解析
resolve: {
    //导入文件查找的限定后缀,默认.js 和.json
    extensions: ['.ts', '.js', '.json' ]
},

PS:导入语句时如果检测不到指定的会报错。

alias 配置可以用别名的方式,将导入路径映射成一个新的路径:

//设置一个别名路径(绝对路径)
alias: {
    css : path.resolve(__dirname, './src/css')
}

require('css/base.css');

modules 配置可以指定 webpack 的解析模块目录:

//找到解析模块目录,如果当前找不到就不断往上一层找
modules: ['node_modules']

//也可以设置指定目录直接查找,检查查找次数
modules: [path.resolve(__dirname, '../node_modules')]

 PS:自然,也可以设置第二参数,防止第一参数找不到报错。

上述配置最终效果:

    resolve: {
        //导入语句省略后缀
        extensions: ['.ts', '.js', '.json'],
        //导入路径的别名
        alias: {
            css: path.resolve(__dirname, './src/css')
        },
        //解析模块目录
        // modules: [path.resolve(__dirname, '../node_modules'), 'node_modules']
    },

十九、Source-Map 配置(devtool映射)

Source-Map 可以将编译、打包、压缩后的代码映射到源代码上,你打包或运行的代码是编译后的,报错后devtool映射能指向你源代码的位置上,它会生成一个.map 文件。

配置方式:

devtool: 'source-map'

根据不同的方式,有下面几种类型:

  • source-map //打包后会生成一个对应的.map 文件
  • inline-source-map //打包后会在 js 文件的内部最后生成 map 内容
  • eval-source-map //打包后会在每个模块都执行 eval
  • hidden-source-map //打包后会生成.map 文件但不会追踪原始文件的错误代码
  • cheap-source-map //打包后会生成.map 并只能精确到行
  • PS:以上打包还可以合并比如 eval-cheap-source-map

在开发环境和生产环境,我们该如何选择?开发要求速度快,调试方便推荐:

  • eval-source-map 或 eval-cheap-source-map //开发环境
  • source-map //生产环境

二十、watch 监听和 clean 清理

1、watch 监听

每次打包,都比较烦,耗时耗力,有一种解决方案:watch 文件监听。这种解决方案,就是你打包时,就挂着监听你原始文件的变化,从而自动更新。

//文件监听,默认 false
watch: true,

//开启 watch,下面才有意义
watchOptions: {
    //不监听解析模块目录
    ignored: /node_modules/,

    //防止更新频率太快,默认 300 毫秒,意味监听到变化后 500 毫秒再编译
    aggregateTimeout: 500,

    //轮询间隔时间,1 秒,询问系统指定文件是否变化了
    poll: 1000
},

2、clean 清理

每次打包之前都要手动清空设置的打包目录,因为不清空的话,改了配置目录就会多出废目录或文件。所以,使用 clean-webpack-plugin 插件,可以解决这个问题。

npm i clean-webpack-plugin -D
//获取 clean 清理插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

//插件
plugins: [
    new CleanWebpackPlugin()
]

二十一、HMR 热替换

页面调试有一个问题,就是当我们编辑了 css 或 js它会自动刷新。但如果有大量的 css 或 js,一个小修改就要全部刷新影响开发性能。而 HMR 热替换就是解决这个问题,当 css 或 js 修改时只刷新修改的部分。对于html 文件,它就一个文件,不需要热替换。开启 HMR 热替换,可以在启动服务时加上--hot,或者在 devServer 配置:

devServer: {
    hot : true
}

对于 css 的热替换,需要使用 style-loader:

{
    test: /\.less$/,
    use: [
        'style-loader',
        'css-loader',
        'less-loader'
    ]
}

当开启了 HMR 热替换之后,发现 html 文件修改后无法自动更新了,此时我们需要在入口文件加入这个 html 文件即可:

entry: [
    './src/js/index.js',
    './src/index.html'
],

对于 js 的热替换,只要在入口文件对加载的其它 js 文件进行设置即可:

import name from './module'
console.log(name);

if (module.hot) {
    module.hot.accept('./module', () => {
        console.log(name);
    })
}

二十二、请求代理设置

这里提供两个远程 json 文件,会返回一些数据用于测试跨域问题:

https://cdn.liyanhui.com/data.json (可跨域,设置过)

https://cdn.ycku.com/data.json (不可跨域,默认)

安装 axios用于 ajax 测试:

npm install axios

编写代码,测试数据:

import axios from 'axios'

axios.get('https://cdn.liyanhui.com/data.json').then(res => {
    console.log(res.data)
})

 如果把地址中的 liyanhui 换成 ycku 的话,会出现跨域的问题。

通过 devServer 设置代理模式,来解决跨域问题:

    devServer: {
        //设置代理
        proxy: {
            //匹配前缀为/api
            '/api': {
                //目标域名 ip
                target: 'https://cdn.ycku.com',
                //改变源
                changeOrigin: true,
                //重写 url, 去掉 /api
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
axios.get('/api/data.json').then(res => { ... }).catch(error => {})

二十三、支持vue单文件组件

安装vue及相关插件

cnpm i [email protected] -S
cnpm i [email protected] [email protected] -D

 webpack.config.js中解构VueLoaderPlugin:

const { VueLoaderPlugin } = require("vue-loader");

 webpack.config.js中加入vue-loader:

{
            test: /\.vue$/, //匹配vue文件
            use: 'vue-loader'
}

及VueLoaderPlugin插件:

plugins: [new VueLoaderPlugin()]

 完整的webpack.config.js:


/**
 * 此文件是webpack配置文件,用于指定webpack去执行哪些任务
 */
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require("vue-loader");
const path = require('path');

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, 'dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: './js/bundle.js'
    },
    mode: 'development',
    //1、所有的loader都要添加在module对象的rules中
    //2、rules是一个数组,数组中的每一个对象就是一个loader
    //3、loader特点:下载后无需引入,只需申明
    module: {
        rules: [{
            test: /\.vue$/, //匹配所有less文件
            use: 'vue-loader'
        }, {
            test: /\.less$/, //匹配所有less文件
            use: [
                'style-loader', //3、用于在html文档中创建一个style标签,将样式“塞”进去
                'css-loader', //2、将less编译后的css转换为CommonJs的一个模块
                'less-loader' //1、将less编译成为css,但不生成单独的css文件,在内存中
            ]
        }, {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }, {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    "presets": [
                        ["@babel/preset-env", {
                            useBuiltIns: "usage", //按需引入需要使用polyfill
                            corejs: { version: 3 }, //解决不能找到core-js的问题
                            targets: { //解决兼容性处理哪些浏览器
                                "chrome": "58",
                                "ie": "9"
                            }
                        }]
                    ],
                    "cacheDirectory": false // 开启babel缓存
                }
            }
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [
                {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        publicPath: './images', //决定图片的url路径
                        outputPath: 'images', //决定文件本地输出路径
                        name: '[hash:5].[ext]' //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
                    }
                }
            ]
        }]
    },
    plugins: [new VueLoaderPlugin()]
};

新建vue相关文件:

src/ App.vue:



src/index.js:

import Vue from 'vue'
import App from './App.vue'

new Vue({
    render: h => h(App)
  }).$mount('#app')
  

dist/index.html:




    
    
    
    Document


    

最后使用”npm run build“ 命令编译后打开”dist/index.html“页面即可查看最终效果。

 二十四:环境分离设置

当配置文件写的越发繁杂时,发现生产环境的配置和开发环境配置有冲突。此时,我们希望有两套配置文件,一套用于开发环境,一套用于生产环境。这里使用js合并插件webpack-merge来实现配置文件的分离保存。

1、创建文件夹config,将webpack.config.js复制两份:

./config/webpack.dev.config.js

./config/webpack.prod.config.js

2、将原先的webpack.config.js改为webpack.base.config.js,然后分离代码:

cnpm i webpack-merge -D
//合并js插件
const{ merge } = require('webpack-merge')

//基础配置
const base = require('./base.config')

//开发环境配置
module.exports = merge(base,{
    ...
})

 webpack.base.config.js:

// 路径
const path = require("path");
// html插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 提取css为单独文件插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  // 入口文件
  entry: "./src/js/index.js", // 由cmd执行目录决定
  // 出口文件
  output: {
    path: path.resolve(__dirname, "../dist"), // 由当前文件所在位置和要生成的dist位置关系决定
    filename: "./js/bundle.js"
  },

  // 模块解析
  resolve: {
    // 导入语句省略后缀
    extensions: [".ts", ".js", ".json", ".css", ".less", ".scss"],
    // 导入路径的别名
    alias: {
      css: path.resolve(__dirname, "./src/css")
    },
    // 解析模块目录
    modules: [path.resolve(__dirname, "../node_modules"), "node_modules"]
  },

  // 模块
  module: {
    rules: [
      {
        test: /\.less$/, // 匹配所有less文件
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: "../"
            }
          },
          "css-loader", // 2、将less编译后的css转换为CommonJs的一个模块
          "less-loader" // 1、将less编译成为css,但不生成单独的css文件,在内存中
        ]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  useBuiltIns: "usage", // 按需引入需要使用polyfill
                  corejs: { version: 3 }, // 解决不能找到core-js的问题
                  targets: {
                    // 解决兼容性处理哪些浏览器
                    chrome: "58",
                    ie: "9"
                  }
                }
              ]
            ],
            cacheDirectory: false // 开启babel缓存,只编译未编译代码,可减少编译时间
          }
        }
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: "url-loader", //url-loader依赖file-loader,使用了url-loader后file-loader可以不用
            options: {
              limit: 8192, //url-loader能将小于8k图片编译成base64,大的采用file-loader处理
              //publicPath: "./images", //决定图片的url路径,less处设置了,此处就不需要了,不然html中的图片路径会出错
              outputPath: "./images", //决定文件本地输出路径
              name: "[hash:5].[ext]" //修改文件名称,[hash:5]取hash值前5位,[ext]文件扩展名
            }
          }
        ]
      },
      {
        test: /\.(html)$/,
        use: {
          loader: "html-loader" // html-loader找到图片后自动交给url-loader处理
        }
      }
    ]
  },
  //插件
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
      filename: "index.html", // 打包后的index.html文件默认位于dist目录下
      minify: {
        removeComments: true, // 是否移除注释 默认 false
        collapseWhitespace: true, // 是否去除空格,默认 false
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true
      }
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css"
    })
  ]
};

webpack.dev.config.js

// 合并js插件
const { merge } = require("webpack-merge");
// 公共基础配置
const base = require("./webpack.base.config");

// 开发环境配置
module.exports = merge(base, {
  // devServer
  devServer: {
    open: true,
    compress: true,
    port: 8080,
    stats: "minimal", //迷你型服务启动信息
    // 代理
    proxy: {
      "/api": {
        target: "https://cdn.ycku.com",
        changeOrigin: true,
        pathRewrite: {
          "^/api": ""
        }
      }
    }
  },

  // devTool
  devtool: "eval-source-map",

  // 开发模式
  mode: "development"
});

 webpack.prod.config.js:

// 合并js插件
const { merge } = require("webpack-merge");
// 公共基础配置
const base = require("./webpack.base.config");
// css压缩插件
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
// 清理打包目录插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = merge(base, {
  //devTool
  devtool: "source-map",

  //生产模式
  mode: "production",
  //监听
  watch: true,

  //监听选项,当true时才有效
  watchOptions: {
    //不监听解析模块目录
    ignored: /node_modules/,
    //更新频率
    aggregateTimeout: 500,
    //轮询
    poll: 1000
  },
  plugins: [
    new OptimizeCSSAssetsPlugin({
      cssProcessorPluginOptions: {
        preset: [
          "default",
          {
            discardComments: {
              removeAll: true
            }
          }
        ]
      },
      cssProcessorOptions: {
        map: {
          inline: false,
          annotation: true
        }
      }
    }),
    new CleanWebpackPlugin()
  ]
});

 修改package.json的指令:

    "dev": "webpack-dev-server --open chrome --port 3000 --config ./config/webpack.dev.config.js",
    "start": "webpack-dev-server --open chrome --port 3000 --config ./config/webpack.dev.config.js",
    "build": "webpack --config ./config/webpack.prod.config.js"
  • 开发环境指令:
npm start
npm run dev
  • 生产环境指令:
npm run build

生产环境代码需要部署到服务器上才能运行,serve这个库能够快速帮我们搭建一个静态资源服务器:

npm i serve -g
serve -s build -p 5000

二十五、webpack中path、outputPath、publicPath联系与区别

path:仅告诉Webpack打包结果存储位置,必须是绝对路径。

outputPath:仅告诉各插件所生成文件的存放位置,该位置以path设置路径为基础,比如:output输出的js、url-loader解析的图片、HtmlWebpackPlugin生成的html文件都会存放在以path为基础的目录下。

publicPath:并不会对生成文件的路径造成影响,主要是在生产模式下,对你的页面里面引入的资源的路径做对应的补全,常见的就是css文件里面引入的图片、html文件里的url值补全。

请注意观察如下webpack.base.config.js文件中publicPath、outputPath及单独css文件夹设置:

不借助脚手架使用webpack4从0开始搭建一个完整vue开发环境_第4张图片

项目路径 

webpack.base.config.js:

const path = require('path')
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') //压缩 css

module.exports = {
    entry: './src/js/index.js', // 由cmd执行目录决定
    output: {
        path: path.resolve(__dirname, '../dist'), // 由当前文件所在位置和要生成的dist位置关系决定
        filename: 'js/bundle.js'
    },
    resolve: {
        //导入语句省略后缀
        extensions: ['.ts', '.js', '.json'],
        //导入路径的别名
        alias: {
            css: path.resolve(__dirname, './src/css')
        },
        //解析模块目录
        // modules: [path.resolve(__dirname, '../node_modules'), 'node_modules']
    },
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            publicPath: '../'
                        }
                    },
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.m?js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                useBuiltIns: "usage", //按需引入需要使用polyfill
                                corejs: { version: 3 }, //解决不能找到core-js的问题
                                targets: { //解决兼容性处理哪些浏览器
                                    "chrome": "58",
                                    "ie": "9"
                                }
                            }]
                        ],
                        "cacheDirectory": false // 开启babel缓存,只编译未编译代码,可减少编译时间
                    }
                }
            },
            {
                test: /\.(png|jpg|gif|jpeg)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        outputPath: 'images',
                        name: '[hash:5].[ext]'
                    }
                }
            },
            {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader'
                }
            },
            {
                test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        outputPath: 'media',
                        name: '[hash:5].[ext]'
                    }
                }]
            }
        ]
    },
    plugins: [new HtmlWebpackPlugin({
        template: "./src/index.html", // 以当前文件为模板创建新的html(1、结构和原来一样;2、会自动引入打包的资源),默认 ,路径由cmd执行目录决定
        filename: "index.html", // 打包后的index.html文件默认位于dist目录下
        minify: {
            removeComments: true, //去掉注释
            collapseWhitespace: true, //去掉空格
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
        }
    }), new MiniCssExtractPlugin({
        filename: 'css/[name].css'
    }), new OptimizeCSSAssetsPlugin({
        cssProcessorPluginOptions: {
            preset: [
                'default',
                {
                    discardComments: {
                        removeAll: true
                    },
                },
            ]
        },
        cssProcessorOptions: {
            map: {
                inline: false,
                annotation: true
            }
        }
    })]
}

二十六、总结

entry: './src/js/index.js', // 由cmd执行目录决定
output: {
    path: path.resolve(__dirname, '../dist'), // 由当前文件(webpack.config.js)所在位置和要生成的dist位置关系决定
    filename: 'js/bundle.js'
}

1、明确生成的dist目录结构,比如./dist/images、./dist/css、./dist/js、./dist/index.html,根据此目录结构来确定配置文件中配置项publicPath的路径。

如果是开发模式,则是./images、./css、./js、./index.html结构。

二十七、项目完整代码

https://gitee.com/duansamve/webpack4-to-cli.githttps://gitee.com/duansamve/webpack4-to-cli.git

你可能感兴趣的:(webpack,webpack)