webpack的學習小計

一、Webpack做爲前端構建工具的用途 文件在webpack01中

代碼轉換:如ts轉換爲js
文件優化:對文件進行壓縮
代碼分割
模塊合併
自動刷新
代碼校驗
自動發佈css

二、安裝webpack webpack-cli工具

命令:html

npm install webpack webpack-cli -D

裏面的核心概念:
入口 : 默認找的是src/index
出口 :自動打包出的文件是dist/main.js
loader
plugins:插件前端

根據運行環境不一樣,js的寫法也不相同,因一個js就是一個模塊。

esModule(瀏覽器環境) commonJS(Node環境)的不一樣:
esModule 引入模塊:import 暴露模塊:export
commonJS 引入模塊:require 暴露模塊:module.exportsnode

簡單打包

查看本地的npm版本webpack

npm -v

如果5.2.0+,即5.2以上版本,能夠直接npx webpack進行打包
這個命令:npx webpack
=> 這個命令是找到node_modules/.bin/webpack命令,內部會去webpack-cli,根據用戶本身的配置,進行解析,隨後執行打包操做。若沒有本身配置,就會找webpack默認的。(用戶配置的文件就是webpack.config.js)
能夠本身配置,package.json:git

"scripts":{
    "dev":"webpack --mode developent",
    "build":"webpack --mode production"
}

運行命令:
npm run dev / npm run build 執行打包es6

插件 plugin:

因webpack支持的插件是少的,因此須要本身手動添加安裝,及配置。使用到的文件是:webpack.config.js。

html-webpack-plugin 插件:將打包好的js自動引入html

安裝命令:github

npm install html-webpack-plugin -D

index.html是模板文件,想將打包好的js自動引入到index.html,不用本身再配置了,就須要在webpack-config.js中引入此插件,使用便可。(開發環境中使用。)
具體使用在webpack.config.js/plugins中設置(先引入,再使用)。 配置完,運行打包命令,就能夠將打包好的js直接引入到html文件中,web

clean-webpack-plugin 插件:清空上次打包好的文件(在這裏是dist)

安裝命令:ajax

npm install clean-webpack-plugin -D

在dist打包成功的文件夾下,可能有多個文件,爲避免混亂,能夠引入這個插件.

webpack-dev-server

建立本地服務器,自動從新構建,自動打開瀏覽器並刷新
安裝命令:

npm install webpack-dev-server -D

下載完後,須要在webpack.config.js文件中進行配置,具體配置:

// devServer自動構建並打開
    devServer: {//在內存中打包,全部的目錄是在根目錄下
        port: 7777,  //端口
        compress: true, //是否壓縮代碼
        open: true, //是否自動打開瀏覽器
        contentBase: "static", //啓動配置一個訪問的靜態資源文件
        hot: true //自動刷新
    },

配置完後,須要在package.json的scripts中,添加dev-server的命令

"scripts": {
    "build": "webpack --mode production",
    "dev": "webpack --mode development",
    "dev:server": "webpack-dev-server"
  },

多入口文件 多出口

<!--  多個入口文件 多出口 -->
    entry: {
        index: "./src/index.js",
        other: "./src/other.js"
    },
    output: {
        filename: '[name].js', //name表明上方的index/other
        path: path.resolve(__dirname, "dist")
    }

若想打包的時候有多個文件,同時引入不一樣的js

能夠定義數組,遍歷

let htmlPlugin = ['index', 'other'].map(chunkName => {
    return new HtmlWebpackPlugin({
        template: `./${chunkName}.html`,
        filename: `${chunkName}.html`,
        chunks: [chunkName]
    })
})

最後用展開運算符執行…htmlPlugin放入plugins數組中 ,詳見最下面的配置代碼

loader 解析文件

直接寫樣式,而後在index.js引入index.css是不生效的,運行npm run dev:server會報錯。

解析CSS:css-loader style-loader

安裝命令:

npm install css-loader style-loader -D

安裝完後,去webpack.config.js中進行配置

module:{
    relus:[
        // 如有多個解析器,可使用數組形式[],單個用字符串形式便可。
        {
            test: /\.css$/,
            // 值能夠是[] 數組形式/ {}對象形式 / ""字符串形式
            use: ['style-loader', 'css-loader']
        }
    ]
}

解析less : less less-loader

安裝命令:

npm install less less-loader -D

安裝後,相似上面的css-loader,也要設置下config文件,若將less文件引入到css中,也須要修改配置文件,詳見webpack.config.js

解析sass :node-sass sass-loader

解析stylus : stylus stylus-loader

postcss-loader 插件:配置CSS3私有前綴

如用到CSS3的新屬性,瀏覽器會有不一樣的前綴,纔可生效,就用到這個postcss-loader(樣式處理工具)
會用到好比autoprefixer(處理私有前綴的插件)
在使用前,先安裝:

npm install postcss-loader autoprefixer -D

安裝後,須要新建一個文件名爲:postcss.config.js的文件,因會調用這個配置文件.
在裏面引入下載的處理私有前綴的文件。
以後,要在webpack.config.js中的css-loader位置,加上這個postcss-loader,加前綴。
在module.exports => module => rules 下寫上:

{
    test: /\.css$/,
    use: [
        'style-loader',//在使用MiniCssExtractPlugin插件分離出css後,這裏的style-loader就不須要了
        {
            loader: 'css-loader',
            options: {
                importLoaders: 2 
            }
        }, 'postcss-loader', 'less-loader' //加在less-loader前面
    ]
},

(能夠運行npm run dev,看打包好的index.js,搜索就能夠看到添加的CSS3屬性的前綴)

若想要覆蓋99.5%的瀏覽器,根目錄下新建一個文件".browserslistrc"

內容爲:cover:99.5%

mini-css-extract-plugin 插件:分離css

安裝命令:

npm install mini-css-extract-plugin -D

安裝後,要在webpack.config.js中引入,並使用。具體使用見webpack.config.js
引入:
而後在plugins使用:

new MiniCssExtractPlugin({
    filename: 'css/main.css' //設置分離出的css的存放目錄及文件名
})

還須要在 配置解析器 module => relus中寫入:

{
    test: /\.css$/,
    use: [
        {
            loader: MiniCssExtractPlugin.loader 
        },
        {
            loader: 'css-loader',
            options: {
                importLoaders: 2 
            }
        }, 'postcss-loader', 'less-loader'
    ]
},

就能夠分離css了(能夠npm run dev 查看,dist下會有一個css/main.css)

optimize-css-assets-webpack-plugin 插件:壓縮css

當項目上線,代碼會進行壓縮,若使用了MiniCssExtractPlugin插件分離css,打包的時候,js會自動壓縮,可是css不會,因此須要安裝單獨的插件來壓縮css文件
安裝命令:

npm install optimize-css-assets-webpack-plugin -D

安裝後,在webpack.config.js中引入:(使用大駝峯命名)

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

而後在與entry,output,module,plugins的同級,寫上:
// webpack中配置壓縮css,js的規定用法:

optimization: {
    minimizer: [//壓縮的css ,js
        new OptimizeCssAssetsWebpackPlugin()
    ]
},

執行npm run build 就能夠看到css,被壓縮了。

可是,js此時就不會自動壓縮了。須要使用terser-webpack-plugin插件(覆蓋默認壓縮工具minimizer).

terser-webpack-plugin 插件 壓縮js

安裝命令:

npm install terser-webpack-plugin

不能放到開發環境下
而後根據壓縮css的使用方法使用便可。
引入:

const TerserJsPlugin = require('terser-webpack-plugin');

使用:

optimization: {
    minimizer: [//壓縮的css ,js
        new OptimizeCssAssetsWebpackPlugin(),
        new TerserJsPlugin()
    ]
},

而後使用npm run build
就能夠看到,css和js都壓縮成功了。

圖片須要的解析器 webpack03中開始

file-loader:把圖片解析成一個文件傳進來

url-loader:把一些小圖片解析成base64插入到頁面中

安裝命令:

npm install file-loader url-loader -D

安裝後,在index.js中,引入一張圖片(已經下載好的),能夠寫以下代碼:

import url from './img1.jpg';
// let oImg = document.createElement('img');
let oImg = new Image();
oImg.src = url;
document.body.appendChild(oImg);

在webpack.config.js中,須要在module中的rules配置flie-loader

{
    test: /\.(jpg|jpeg|gif)$/,
    use: {
        loader: 'file-loader',
        options: {
            name: 'img/[name].[ext]'
        }
    }
}

打包npm run dev時,就能夠把這個圖片當作一個文件傳進去了

如果圖片不大的話,能夠選擇使用url-loader,轉換爲base64的格式
index.js中的引入不變,變化的是webpack.config.js中module=>rules的內容:

{
    test:/\.(jpg|jpeg|gif)/,
    use:{
        loader:'url-loader', //小於100kb的使用url-loader
        options:{
            limit:100*1024, //100kb
            outputPath:'img',
            publicPath:'http://www.baidu.com/img' //這能夠寫網上的地址
        }
    }
}

對於圖標:iconfont

官網地址:https://www.iconfont.cn/?spm=a313x.7781069.1998910419.d4d0a486a
使用方法:
挑選使用的iconfont,將挑選好的iconfont,能夠新建一個項目,直接下載到本地(新建的項目是指在iconfont網中的添加項目,須要下載到本地)
把須要的文件(iconfont.css & iconfont.eot & iconfont.svg & iconfont.ttf & iconfont.woff & iconfont.woff2)放入到項目中src中
要在項目index.js中引入這個下載好的圖庫

import './icon/iconfont.css'; //引入iconfont的樣式文件
let i = document.createElement('i');
i.className = 'iconfont icon-agreement';
document.body.appendChild(i);

而後從新配置下webpack.config.js文件下的module=>relus:

{
    test:/\.(eot|svg|ttf|woff|woff2)$/,
    use:'file-loader'
}

從新npm run devServer 就能夠在頁面中看到這個iconfont了

將ES6,ES7轉換爲全部瀏覽器都支持的ES5

在webpack中,一個重要的模塊,此功能使用到的插件有:

@babel/core babel的核心模塊

babel-loader 解析js代碼,webpack和babel的橋樑

@babel/preset-env es6轉化爲es5插件的集合

安裝命令:

npm install @babel/core babel-loader @babel/preset-env -D

安裝完後,配置webpack.config.js中的module=>rules:

{
    test: /\.js$/,
    use: 'babel-loader',
    include: path.resolve(__dirname, 'src'), //須要編譯的js文件的目錄
    exclude: 'node_modules' //排除須要編譯js文件的目錄
}

運行打包npm run dev,發現打包出的文件中仍是箭頭函數,因此須要配置babel的預設。
建立一個文件名爲:.babelrc,裏面的內容爲:

{
    // preset 預設(插件的集合)從下往上 
    "presets":[
        "@babel/preset-env"
    ],
    // plugins 一個個插件,從上往下
    "plugins":[]
}

再去執行npm run dev ,就會看到箭頭函數已經能夠解析成功了,可是有些ES6的方法,依然是不解析的,須要另外配置參數。
舉個栗子:
index.js中寫入以下代碼:

console.log('nihaoshijie'.includes('g'));

npm run dev ,在其中查找includes,這個方法仍是這樣寫的,沒有解析
因此須要修改.babelrc值:

{
    //  preset 預設(插件的集合)從下往上 
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage" //只轉化使用的api
            }
        ]
    ],
    // plugins 一個個插件,從上往下 
    "plugins": []
}

修改後,npm run dev
會提示要去下載core-js

會要求下載core-js@3 這個至關於以前用如今廢棄的@babel/pollyfill

這時根據提示下載:npm install --save core-js@3
而後在進行配置.babelrc的內容,修改成:

{
    //  preset 預設(插件的集合)從下往上 
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage", //只轉化使用的api
                "corejs": 3 //至關於以前用的@babel/pollyfill(已廢棄)功用是轉化es6中高版本的api(好比promise,async,await這種)
            }
        ]
    ],
    // plugins 一個個插件,從上往下 
    "plugins": []
}

再運行npm run dev,在打包好的文件中,搜索includes,就能夠看到已經給轉化爲瀏覽器均可識別的es5方法

<!-- 裝飾器 -->
在index.js中寫一個裝飾器的例子:
//草案
// 裝飾器 語法糖(吃起來甜,理解有些難)
@fn
class Son{
    a = 1
}
function fn(target) {
    target.flag = true;
    console.log(target);
}
let S = new Son();
console.log(Son.flag);

運行 npm run dev,
會報錯,因不能識別,須要用到下列的包

@babel/plugin-proposal-class-properties 修飾類,還有一個@babel/plugin-proposal-decorators 修飾裝飾器的

如有一些草案的語法,須要修飾器,用到一些包:
安裝:npm install @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D
安裝後,配置寫**.babelrc**文件內容爲:

{
    //  preset 預設(插件的集合)從下往上 
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage", //只轉化使用的api
                "corejs": 3 //至關於以前用的@babel/pollyfill(已廢棄)轉化es6中高版本的api
            }
        ]
    ],
    // plugins 一個個插件,從上往下 
    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true //若爲false使用舊版本,用true使用最新的
            }
        ],
        [
            "@babel/plugin-proposal-class-properties",
            {
                "loose": true //解析類的屬性,默認爲false。若用true是使用表達式
            }
        ]
    ]
}

寫完後,運行:npm run dev:server ,就能夠識別,而且會有打印值。

/*index.js中打印到的值
    function Son() {
        _classCallCheck(this, Son);
        this.a = 1;
    }
    true
*/

能夠在sum.js中也寫上一個方法,使用修飾器的例子:

class Person {
    @readonly
    first = 1;
}
function readonly(target, name, descriptor) {
    descriptor.writable = false; //不可更改
}
let p = new Person();
p.first = 100; 
//sum.js中,因已經寫了不可更改,使用了修飾器修飾first
//會獲得以下錯誤:Uncaught TypeError: Cannot assign to read only property 'first' of object '#<Person>'

@babel/plugin-transform-runtime @babel/runtime 優化js

每一個js文件中有方法,在打包時會出現_classCallCheck方法,10個這種文件就會有10個_classCallCheck,因此會有冗餘代碼。想要優化js,會用到: @babel/plugin-transform-runtime @babel/runtime兩個插件
安裝:

npm install  @babel/plugin-transform-runtime @babel/runtime -D

安裝後,配置.babelrc:

{
    //  preset 預設(插件的集合)從下往上 
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage", //只轉化使用的api
                "corejs": 3 //至關於以前用的@babel/pollyfill(已廢棄)轉化es6中高版本的api
            }
        ]
    ],
    // plugins 一個個插件,從上往下 
    "plugins": [
        "@babel/plugin-transform-runtime", //這是優化js,去除每次調用的封裝方法會使用helpers的方法,輕便,會自動查找@babel/runtime,故不須要引入
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true //若爲false使用舊版本,用true使用最新的
            }
        ],
        [
            "@babel/plugin-proposal-class-properties",
            {
                "loose": true //解析類的屬性,默認爲false。若用true是使用表達式
            }
        ]
    ]
}

當配置好後,運行npm run dev,查看打包好的bundle.js,會發現,以前的_classCallCheck方法都變成了_babel_runtime_helpers_classCallCheck來轉換類,再也不是哪一個文件有類就封裝,並且bundle.js大小也發生變化。
若無這行"@babel/plugin-transform-runtime",bundle.js是89kib
有"@babel/plugin-transform-runtime"的話,bundle.js是12.1kib

跨域問題:

須要一個後臺返回數據,可使用express
安裝下:

npm install express -D

而後新建一個server.js文件,中間寫上可成功運行的代碼:

let express = require('express');
let app = express();
app.get('/user', function (req, res) {
    res.json({
        name: 'zoe'
    })
})
app.listen(8000, function () {
    console.log('8000服務已啓動')
})

前端的服務端口號是7777,後臺是8000,能夠在index.js中,發送ajax,把以前代碼都註釋

let xhr = new XMLHttpRequest();
xhr.open('get', '/api/user', true); //異步
xhr.onreadystatechange = function () { //監聽請求是否成功,成功打印數據
    console.log(xhr.response);
}
xhr.send();

啓動服務:

npm run dev:server

此時頁面中會報錯:HTTP404: 找不到 - 服務器還沒有找到與請求的 URI (統一資源標識符)匹配的任何內容。(XHR)GET - http://localhost:7777/api/user,由於跨域了。
要想解決這個問題,須要在webpack.config.js中的devServer中,配置proxy,就是代理,來解決跨域,配置以下,直接在devServer中寫:

devServer: {
    port: 7777,
    hot: true,
    contentBase: "static",
    open: true,
    proxy: { //啓動代理,解決跨域
        '/api': { //請求地址以api開頭
            target: 'http://localhost:8000', //請求的服務器地址
            secure: false, //若爲true,表示以https開頭
            pathRewrite: { '^/api': '' }, //重寫請求的地址
            changeOrigin: true, //把請求頭中的host改爲服務器的地址
        }
    }
}

修改完配置信息,從新npm run dev:server 就能夠得到數據

若不須要後端配合,前端本身也能夠實現:
devServer.before 提供在服務器內部全部其餘中間件以前執行自定義中間件的能力。這可用於定義自定義處理程序。將以上寫的proxy註釋,並添加以下內容:

before: function (app, server) { //表示啓動一個端口號爲7777服務
    app.get('/api/user', function (req, res) {
        res.json({ custom: 'response' });
    });
}

配置完以上代碼,就能夠重啓 npm run dev:server,就可解決跨域問題。
以上是解決跨域的兩種方法,看後端是否有地址,有的話,直接配置,沒有能夠本身實現。

附一個webpack.config.js文件

// 配置文件
// 遵循node中的commonJS規範,引入文件用require
const path = require('path');
// console.log(path.resolve(__dirname, "dist"));  //E:\Users\zoe\Desktop\學習js\zhufeng\webpack01\dist  ----path.resolve()將地址轉成絕對路徑
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); //功用:每次打包清空文件夾
const HtmlWebpackPlugin = require('html-webpack-plugin'); //功用:將打包好的js文件自動引入到html中
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //插件功用:分離css的插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); //插件功用:壓縮css
const TerserJsPlugin = require('terser-webpack-plugin'); //插件功用:壓縮js

let htmlPlugin = ['index', 'other'].map(chunkName => {
    return new HtmlWebpackPlugin({
        template: `./${chunkName}.html`,
        filename: `${chunkName}.html`,
        chunks: [chunkName]
    })
})
module.exports = {
    // entry: "./src/index.js", //單個的入口文件
    // output: {
    //     // 文件指紋:hash(整個項目產生的hash) chunkHash(根據出口文件產生的hash) contentHash(根據內容產生的hash)
    //     // 爲防止緩存,加個hash
    //     filename: 'bundle.js',
    //     path: path.resolve(__dirname, "dist")
    // },
    // 多個入口文件 多出口
    entry: {
        index: "./src/index.js",
        other: "./src/other.js"
    },
    output: {
        filename: '[name].js', //name表明上方的index/other
        path: path.resolve(__dirname, "dist")
    },
    // webpack中配置壓縮css,js的規定用法:
    optimization: {
        minimizer: [//壓縮的css ,js
            new OptimizeCssAssetsWebpackPlugin(), //壓縮Css
            new TerserJsPlugin()  //壓縮的js
        ]
    },
    // 配置解析器:
    module: {
        // 需先加載css- loader,再加載style-loader
        rules: [
            // 這裏的順序是: 從下往上 從右往左,因此寫的時候要注意順序
            // 使用對象{}寫法:
            // {
            //     test: /\.css$/,
            //     use: 'css-loader'
            // },
            // {
            //     test: /\.css$/,
            //     use: 'style-loader',
            //     enforce: 'post' // pre優先加載 post 最後加載。
            // }
            // 如有多個解析器,可使用數組形式[],單個用字符串形式便可。
            {
                test: /\.css$/,
                // 值能夠是[] 數組形式/ {}對象形式 / ""字符串形式
                // 若在index.css中引入a.less,要配置以下:
                use: [
                    // 'style-loader',在使用MiniCssExtractPlugin插件分離出css後,會經過外鍊形式引入到頁面,因此這裏的style-loader就不須要了
                    {
                        loader: MiniCssExtractPlugin.loader //寫完存放分離出css路徑後還須要在此處調用一下loader(插件用法)
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 2 //用後面的1個加載器來解析,如有2個就寫2
                        }
                    }, 'postcss-loader', 'less-loader'
                ]
            },
            {
                test: /\.less$/,
                use: ['style-loader', 'css-loader', 'less-loader'] //因是從右向左,能夠先寫右邊的,而後在往前面加
            }
        ]
    },
    // devServer自動構建並打開
    devServer: {//在內存中打包,全部的目錄是在根目錄下
        port: 7777,  //端口
        compress: true, //是否壓縮代碼
        open: true, //是否自動打開瀏覽器
        contentBase: "static", //啓動配置一個訪問的靜態資源文件
        hot: true, //自動刷新
        // proxy: { //啓動代理,解決跨域
        //     '/api': { //請求地址以api開頭
        //         target: 'http://localhost:8000', //請求的服務器地址
        //         secure: false, //若爲true,表示以https開頭
        //         pathRewrite: { '^/api': '' }, //重寫請求的地址
        //         changeOrigin: true, //把請求頭中的host改爲服務器的地址
        //     }
        // }
        before: function (app, server) { //表示啓動一個端口號爲7777服務,不須要後端配合就能夠解決跨域
            app.get('/api/user', function (req, res) {
                res.json({ custom: 'response' });
            });
        }
    },
    // 插件在這裏設置
    plugins: [
        // new CleanWebpackPlugin({
        //     cleanOnceBeforeBuildPatterns: ['cc/*', '!cc/a.js']
        // }) //清空cc下的全部文件,除了cc/a.js
        new CleanWebpackPlugin(), //清空輸出的目錄
        new MiniCssExtractPlugin({
            filename: 'css/main.css' //設置分離出的css的存放目錄及文件名
        }),
        // 多的話就須要封裝函數:直接展開定義的實例化例子
        ...htmlPlugin
        // 如有少的html,能夠這麼寫
        // new HtmlWebpackPlugin({
        //     // 打包好的js插入到哪一個文件就使用template字段
        //     template: "./index.html",//依賴的模板文件
        //     hash: true, //能夠加hash,在打包好的文件中引入的js中有hash值
        //     minify: { //打包的時候去掉一些東西
        //         removeAttributeQuotes: true,//刪除引號
        //         collapseWhitespace: true//刪除空格
        //     },
        //     filename: "index.html", //打包後的html文件名
        //     chunks: ['index'] //指定引入的入口文件是哪一個
        // }),
        // new HtmlWebpackPlugin({
        //     // 打包好的js插入到哪一個文件就使用template字段
        //     template: "./other.html",//依賴的模板文件
        //     hash: true, //能夠加hash,在打包好的文件中引入的js中有hash值
        //     filename: "other.html", //打包後的html文件名
        //     chunks: ['other']
        // })
    ]
}

相關網址:
webpack官網
上述demo地址:webpack01Demo

本文同步分享在 博客「zoepriselife316」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索