學(xué)習(xí)webpack能不能翻一下身??(一)

webpack+react

https://juejin.im/post/5da5748851882555a8430641
本咸魚是根據(jù)這一篇博客來的的,同時我也記錄了一些我踩過的坑

建一個空文件夾 webpack-react

  • 命令行執(zhí)行
    npm init -y
    // 生成一個package.json 
    // npm install就是根據(jù)這個文件去下載我們項(xiàng)目里使用的包
    // 也就是配置項(xiàng)目所需的運(yùn)行和開發(fā)環(huán)境。

之后我們安裝 webpack

npm i webpack webpack-cli -D 
  • 文件目錄

webpack-react
|- node_modules
|- package.json

接下來,我們在根目錄下新建一個文件夾名為 config 用于存放配置文件,在此文件夾下創(chuàng)建一個 .js 文件名為 webpack.common.config.js ,敲入以下代碼:

// webpack 配置是標(biāo)準(zhǔn)的 Node.js的CommonJS 模塊,
const path = require('path');

module.exports = {
  // 入口文件路徑
  entry: {
    app: './src/app.js',
  },
  // 出口文件
  output: {
    filename: 'js/bundle.js',
    // dist 必須為絕對路徑
    path: path.resolve(__dirname, '../dist')
    publicPath:"/",
   // 和后面dev配合使用,單頁面應(yīng)用路由
  }
}

之后我們創(chuàng)建src文件,并在里面創(chuàng)建app.js

  webpack-react-scaffold
+ |- config
+     |- webpack.common.config.js
  |- node_modules
+ |- src
+     |- app.js
  |- package.json

在package.json中配置

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    // 這里我們定義 --config 我們的webpack配置文件的路徑 默認(rèn)是根目錄文件下的webpack.config.js文件,
    // 為了代碼結(jié)構(gòu)清晰我們建立了config文件夾
    // 并且我們知道為什么是 npm run build ,你不歡喜build,那么打包的命令可以start "start": "webpack --config ./config/webpack.common.config.js"
+  "build": "webpack --config ./config/webpack.common.config.js"
  },

好了,我們試試怎么打包吧。
在控制臺中輸入命令:

    npm run build

執(zhí)行之后,你會發(fā)現(xiàn)根目錄多出了一個文件夾: dist/js ,其中有一個js文件: bundle.js ,那么至此,我們已經(jīng)成功編譯打包了一個js文件,即入口文件: app.js 。

使用webpack-merge

我們將使用一個名為 webpack-merge 的工具。通過"通用"配置,我們不必在環(huán)境特定(environment-specific)的配置中重復(fù)代碼。簡單來說就是生產(chǎn)環(huán)境不同,我們要給的配置也有所不同,但是可以共用一個共有的配置。

安裝

    npm install --save-dev webpack-merge

之后我們在config中建立webpack.dev.config.js和webpack.prod.config.js

prod:生產(chǎn)環(huán)境 production
dev:開發(fā)環(huán)境 development

現(xiàn)目錄結(jié)構(gòu):

  webpack-react-scaffold
  |- config
     |- webpack.common.config.js
+    |- webpack.prod.config.js
+    |- webpack.dev.config.js
  |- node_modules
  |- src
     |- app.js
  |- package.json

在webpack.prod.config.js里輸入代碼:

const merge = require('webpack-merge');
const common = require('./webpack.common.config.js');

module.exports = merge(common, {
  // 生產(chǎn)環(huán)境 可選production(默認(rèn))/ development / none
  mode: 'production',
  
});

回到我們之前創(chuàng)建的 app.js 文件,輸入代碼:

var root =document.getElementById('root');
root.innerHTML = 'hello, webpack!';

在根目錄下創(chuàng)建一個文件夾名為: public ,再新建一個html文件,名為: index.html ,以下內(nèi)容:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>從零配置webpack4+react腳手架</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

html-webpack-plugin

index.html應(yīng)該自動編譯到dist目錄,并且所有的js引用是自動添加的。你可以使用html-webpack-plugin插件來處理這個優(yōu)化。

    npm install html-webpack-plugin -D
    // -D是--save-dev的縮寫

在webpack.prod.config.js中配置plugins屬性

const merge = require('webpack-merge');
const common = require('./webpack.common.config.js');

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

module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      // 這里有小伙伴可能會疑惑為什么不是 '../public/index.html'
      // 我的理解是無論與要用的template是不是在一個目錄,都是從根路徑開始查找
      template: 'public/index.html',
      inject: 'body',
      minify: {
        removeComments: true,
        collapseWhitespace: true,
      },
    })
  ]
});

  • filename:打包之后的html文件名字
  • template:以我們自己定義的html為模板生成,不然我們還要到打包之后的html文件中寫
  • inject:在body最底部引入js文件,如果是head,就是在head中引入js
  • minify:壓縮html文件,更多配置點(diǎn)我
    • removeComments:去除注釋
    • collapseWhitespace:去除空格

現(xiàn)在我們再來打包試試,看看dist中是不是多出了html文件,并且自動引入了script,用瀏覽器打開它試試看是不是能正確輸出內(nèi)容了!

安裝react

    npm install --save react react-dom

安裝完成之后,我們就可以寫react的JSX語法了。

這里為了和react官方腳手架 create-react-app 的目錄結(jié)構(gòu)相類似,我們在 src 文件夾下新建一個js文件, index.js ,用于渲染根組件。index.js輸入:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

用jsx重寫app.js:

import React from 'react';

function App() {
  return (
    <div className="App">Hello World</div>
  );
}

export default App;

并 webpack.common.config.js 文件中的入口進(jìn)行修改,因?yàn)槲覀儸F(xiàn)在要編譯打包的應(yīng)該 index.js :

  const path = require('path');

  module.exports = {
    entry: {
-     app: './src/app.js',
+     index: './src/index.js',
    },
    output: {
      filename: 'js/bundle.js',
      path: path.resolve(__dirname, '../dist')
    }
  }

現(xiàn)在嘗試一下重新運(yùn)行 npm run build ,會發(fā)現(xiàn)打包失敗了,為什么呢?接著看.....

因?yàn)閣ebpack根本識別不了jsx語法,那怎么辦?使用loader對文件進(jìn)行預(yù)處理。
其中,babel-loader,就是這樣一個預(yù)處理插件,它加載 ES2015+ 代碼,然后使用 Babel 轉(zhuǎn)譯為 ES5。那開始配置它吧!

npm install --save-dev babel-loader @babel/preset-react @babel/preset-env @babel/core
  • babel-loader:**使用Babel和webpack來轉(zhuǎn)譯JavaScript文件。
  • @babel/preset-react:**轉(zhuǎn)譯react的JSX
  • @babel/preset-env:**轉(zhuǎn)譯ES2015+的語法
  • @babel/core:**babel的核心模塊

理論上我們可以直接在 webpack.common.config.js 中配置"options",但最好在當(dāng)前根目錄,注意,一定要是根目錄?。?! 新建一個配置文件 .babelrc 配置相關(guān)的"presets":

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          // 大于相關(guān)瀏覽器版本無需用到 preset-env
          "edge": 17,
          "firefox": 60,
          "chrome": 67,
          "safari": 11.1
        },
        // 根據(jù)代碼邏輯中用到的 ES6+語法進(jìn)行方法的導(dǎo)入,而不是全部導(dǎo)入
        "useBuiltIns": "usage"
      }
    ],
    "@babel/preset-react"
  ]
}

再修改webpack.common.config.js

const path = require('path');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    filename: 'js/bundle.js',
    path: path.resolve(__dirname, '../dist')
  },
  module: {
    rules: [
      {
        //test 規(guī)定了作用于以規(guī)則中匹配到的后綴結(jié)尾的文件
        test: /\.(js|jsx)$/,
        // use使用什么插件
        use: 'babel-loader',
        // exclude 告訴我們不需要去轉(zhuǎn)譯"node_modules"這里面的文件。
        exclude: /node_modules/,
      }
    ]
  }
}

我們再次打包:

npm run build

我們再確認(rèn)一次我們的目錄:

  webpack-react-scaffold
   |- config
      |- webpack.common.config.js
      |- webpack.prod.config.js
      |- webpack.dev.config.js
   |- node_modules
   |- public
      |- index.html
   |- src
+     |- index.js
      |- app.js
+  |- .babelrc
   |- package.json

給打包出的js文件換個不確定名字

這個操作是為了防止因?yàn)闉g覽器緩存帶來的業(yè)務(wù)代碼更新,而頁面卻沒變化的問題,你想想看,假如客戶端請求js文件的時候發(fā)現(xiàn)名字是一樣的,那么它很有可能不發(fā)新的數(shù)據(jù)包,而直接用之前緩存的文件,當(dāng)然,這和緩存策略有關(guān)。

很簡單,[hash]或[chunkhash],這里我們先使用hash
修改webpck.common.config.js:

const path = require('path')
module.exports = {
    entry: {
        index: "./src/index.js",
    },
    output: {
        filename: "js/[name].bundle.[hash:4].js",
        path: path.resolve(__dirname, "../dist")
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                use: 'babel-loader',
                exclude: /node_modules/
            }
        ],
    },
}

name是 entry 的文件名 key,而不是文件名.
現(xiàn)在你重新打包,去看看生成的js文件的名字~

打包編譯前清理dist目錄

之前打包的dist里因?yàn)閖s文件名字不同已經(jīng)有了多個js文件,我們只想要最新打包編譯的文件,就需要先清除dist目錄,再重新生成。

安裝

npm i clean-webpack-plugin -D

clean-webpack-plugin配置文檔

修改webpack.prod.config.js:

const merge = require('webpack-merge')
const common = require('./webpack.common.config.js')

const HttpWebpackPlugin = require('http-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = merge(common, {
    mode: 'production',
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            // 這里有小伙伴可能會疑惑為什么不是 '../public/index.html'
            // 我的理解是無論與要用的template是不是在一個目錄,都是從根路徑開始查找
            template: 'public/index.html',
            inject: 'body',
            minify: {
                removeComments: true,
                collapseWhitespace: true,
            },
        }),
        new CleanWebpackPlugin({
            //現(xiàn)在已經(jīng)不需要再寫參數(shù)了,如果不起作用可以試試寫上參數(shù)
            //cleanOnceBeforeBuildPatterns:[path.resolve(process.cwd(),"dist/*")]
        })
    ]
});

現(xiàn)在試試應(yīng)該就只有一個js文件了

代碼分割

這個打包之后的bundle.js文件大小為129kb,隨著業(yè)務(wù)代碼越來越多,這個包會變得越來越大,你每次修改了代碼并發(fā)布,用戶都需要重新下載這個包,但是想想看,我們修改的代碼只是整個代碼的一小部分,還有許多其他不變的代碼,例如 react 和 react-dom ,那我們把這部分不變的代碼單獨(dú)打包。

修改 webpack.common.config.js ,增加一個入口:

  entry: {
    index: './src/index.js',
    framework: ['react','react-dom'],
  },

重新打包,發(fā)現(xiàn)react和react-dom 被編譯成framework.js,但是我們的index.bundle.js還是129kb,沒有變過。
這是因?yàn)槲覀冞€沒有抽離index.js中的公共代碼。

webpack3版本是通過配置CommonsChunkPlugin插件來抽離公共的模塊。webpack4版本,官方廢棄了CommonsChunkPlugin,而是改用配置optimization.splitChunks的方式,更加方便。

添加代碼至 webpack.prod.config.js :

// 代碼太多就不重復(fù)了,根據(jù)上下文將代碼放在合適的地方
module.exports = merge(common,{
    mode:"production",
    plugins:[],
    optimization: {
        splitChunks: {
        chunks: 'all',
        minSize: 30000,
        maxSize: 0,
        minChunks: 1,
        //cacheGroups對象,定義了需要被抽離的模塊
        cacheGroups: {
        framework: {
            //test屬性是比較關(guān)鍵的一個值,他可以是一個字符串,也可以是正則表達(dá)式,還可以是函數(shù)。如果定義的是字符串,會匹配入口模塊名稱,會從其他模塊中把包含這個模塊的抽離出來。
            test: "framework",
            //name是抽離后生成的名字,和入口文件模塊名稱相同,這樣抽離出來的新生成的framework模塊會覆蓋被抽離的framework模塊,雖然他們都叫framework。
            name: "framework",
            enforce: true
        },
        //vendors這個緩存組,它的test設(shè)置為 /node_modules/ 表示只篩選從node_modules文件夾下引入的模塊,所以所有第三方模塊才會被拆分出來。
        vendors: {
            priority: -10,
            test: /node_modules/,
            name: "vendor",
            enforce: true,
        },
      }
    }
  },
})

重新打包,你會發(fā)現(xiàn)沒有效果

    output: {
         // hash 導(dǎo)致了這里打包freamework依舊重新打包
        filename: "js/[name].bundle.[chunkhash:4].js",
        path: path.resolve(__dirname, "../dist")
    },

再此打包
我們發(fā)現(xiàn)index.bundle.js文件大小只有:1.7kb
我們隨意修改一下app.js中的內(nèi)容再打包一次,你會發(fā)現(xiàn)index.bundle.js(不被緩存)的hash值變了,但是freamework.bundle.js(能被緩存)的hash值沒變

壓縮JS文件

npm install uglifyjs-webpack-plugin --save-dev

將uglifyjs-webpack-plugin引入webpack.prod.config.js

const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
// optimization內(nèi)配置minimizer參數(shù)
module.exports = merge(common,{
    mode:
    plugin:[],
    optimization:{
        minimizer:[new UglifyjsWebpackPlugin()]
        splitChunks:{},
    }
})

重新打包編譯看看~我們的index.bundle.js減少了5字節(jié),當(dāng)然,隨著業(yè)務(wù)代碼越來越多,這部分差距會漸漸變大。

自動編譯打包 webpack-dev-server

npm i webpack-dev-server -D

我們每次修改代碼,查看結(jié)果都要經(jīng)歷以此 npm run build ,大大降低了開發(fā)效率,這難以忍受!
webpack給我們提供了devServer開發(fā)環(huán)境,支持熱更新

代碼增加到 webpack.dev.config.js

const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.config.js');

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

module.exports = merge(common, {
  mode: 'development',
  output: {
    filename: 'js/[name].[hash:4].bundle.js',
  },
  devServer: {
    contentBase: path.resolve(__dirname, '../dist'),
    open: true,
    port: 8080,
      compress: true,
    // 設(shè)置devServer.hot為true,并且在plugins中引入HotModuleReplacementPlugin插件即可。
    // 還需要注意的是我們開啟了hot,那么導(dǎo)出不能使用chunkhash,需要替換為hash。
    // 之前我們試過hash會不停的更換名字
    hot: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: 'body',
      hash: false
    }),
    //HotModuleReplacementPlugin是webpack熱更新的插件,設(shè)置devServer.hot為true
    new webpack.HotModuleReplacementPlugin()
  ]
});

之后再修改package.json的啟動start

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./config/webpack.prod.config.js",
    "start": "webpack-dev-server --inline --config ./config/webpack.dev.config.js"
  },

好了,我們現(xiàn)在又可以快樂的 npm run start 啟動!?。?/p>

現(xiàn)在js,html,css,現(xiàn)在前兩架馬車已經(jīng)沒什么問題,我們現(xiàn)在解決css的問題

先讓CSS跑起來

首先我們新建css文件 app.css 并在app中引入

.App{
    color: #ff0000;
}

之后我們發(fā)現(xiàn)我們的8080已經(jīng)報(bào)錯了,wbpack只能編譯js文件,css文件是無法被識別并編譯的,我們需要loader加載器來進(jìn)行預(yù)處理。 首先安裝 style-loader 和 css-loader :

npm i style-loader css-loader -D

遇到后綴為.css的文件,webpack先用css-loader加載器去解析這個文件,遇到“@import”等語句就將相應(yīng)樣式文件引入(所以如果沒有css-loader,就沒法解析這類語句),最后計(jì)算完的css,將會使用style-loader生成一個內(nèi)容為最終解析完的css代碼的style標(biāo)簽,放到head標(biāo)簽里。
loader是有順序的,webpack肯定是先將所有css模塊依賴解析完得到計(jì)算結(jié)果再創(chuàng)建style標(biāo)簽。因此應(yīng)該把style-loader放在css-loader的前面(webpack loader的執(zhí)行順序是從右到左)。

打包出CSS獨(dú)立文件

在我們平時寫css的時候不會將css直接寫在html頁面而是用link標(biāo)簽引入文件,所以我們也希望通過引入外部css文件進(jìn)行樣式引入css,我們需要用到mini-css-extract-plugin這個插件

webpack 4.0以后,官方推薦使用extract-text-webpack-plugin插件來打包c(diǎn)ss文件??梢宰约毫私?/p>

安裝

npm i mini-css-extract-plugin -D

然后將你的webpack.prod.config.js修改

const path = require("path")
const merge = require("webpack-merge");
const common = require("./webpack.common.config");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require("clean-webpack-plugin")
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// ++ 是 minicssexractplugin添加的內(nèi)容
module.exports = merge(common, {
    mode: "production",
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
++                  MiniCssExtractPlugin.loader,
                    "css-loader"
                ]
            }
        ]
    },
    plugins: [
        /*
    
            filename:打包之后的html文件名字
            template:以我們自己定義的html為模板生成,不然我們還要到打包之后的html文件中寫
            inject:在body最底部引入js文件,如果是head,就是在head中引入js,false不生成js
            minify:壓縮html文件,更多配置點(diǎn)我
                removeComments:去除注釋
                collapseWhitespace:去除空格
        */
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: "public/index.html",
            inject: "body",
            minify: {
                removeComments: true,
                collapseWhitespace: true,
            }
        }),
        new CleanWebpackPlugin({
            // 項(xiàng)目里有改動才會有新的包 打包編譯前清理dist目錄
            cleanOnceBeforeBuildPatterns: [path.resolve(process.cwd(), "dist/*")]
        }),
        new MiniCssExtractPlugin({
            // 這里我們注意一下 在開發(fā)環(huán)境中 我們不加上hash
            // 在生產(chǎn)環(huán)境 我們使用hash
++          filename: 'css/[name].[hash].css',
++          chunkFilename: 'css/[id].[hash].css',
        })
    ],
    optimization: {
        minimizer:[
            new UglifyjsWebpackPlugin()
        ],
        splitChunks: {
            chunks: 'all',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            cacheGroups: {
                framework: {
                    test: "framework",
                    name: "framework",
                    enforce: true
                },
                vendors: {
                    priority: -10,
                    test: /node_modules/,
                    name: "vendor",
                    enforce: true,
                },
            }
        }
    },
})

然后我們 npm run build 打包發(fā)現(xiàn)dist中多了css/index.html,你的css已經(jīng)被打包好了
但是它沒有被壓縮

壓縮打包出的CSS文件

壓縮,我們需要 optimize-css-assets-webpack-plugin 插件

安裝

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

修改webpack.prod.config.js

// 在optimization的minimizer中添加參數(shù)
// OptimizeCssAssetsWebpackPlugin
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = merge(common,{
    optimization: {
        minimizer:[
            new UglifyjsWebpackPlugin(),
            new OptimizeCssAssetsWebpackPlugin({
                // 正則表達(dá)式,用于匹配需要優(yōu)化或者壓縮的資源名。默認(rèn)值是/.css$/g  
                assetNameRegExp:/\.css$/g,
                // 用于壓縮和優(yōu)化CSS 的處理器,默認(rèn)是 cssnano.
                cssProcessor:require("cssnano"),
                //  傳遞給cssProcessor的插件選項(xiàng),默認(rèn)為{}
                cssProcessorPluginOptions:{
                //  預(yù)設(shè): '默認(rèn)',         丟棄注釋:     刪除全部注釋 
                preset:['default', { discardComments: { removeAll:true } }]
                },
                //表示插件能夠在console中打印信息,默認(rèn)值是true
                canPrint:true,
            }),
        ],
    }
})
    

另外,這段配置也是可以放到 plugins 這個屬性下進(jìn)行配置的。 配置完成,執(zhí)行 npm run build ,查看dist目錄下打包出的css文件是不是代碼被壓縮了!

Sass and Less

我們寫項(xiàng)目的時候沒幾個人會去寫css吧?sass或less對于工作效率的提高是肉眼可見的,但是我們webpack也同樣無法理解這種編寫方式,那就需要配置loader做預(yù)處理,將其轉(zhuǎn)換為css。

安裝

npm install --save-dev less less-loader node-sass sass-loader

之后我們開始配置 moudule中的rules loader

module: {
    rules: [
      //...
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(sass|scss)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
    
  },

好的,我們執(zhí)行 npm run build 就可以看到sass,less已經(jīng)被轉(zhuǎn)換成css了

我們還需要對css進(jìn)行補(bǔ)全Postcss

postcss 一種對css編譯的工具,類似babel對js的處理,常見的功能如:

  1. 使用下一代css語法
  2. 自動補(bǔ)全瀏覽器前綴
  3. 自動把px代為轉(zhuǎn)換成rem
  4. css 代碼壓縮等等
    postcss 只是一個工具,本身不會對css一頓操作,它通過插件實(shí)現(xiàn)功能,autoprefixer 就是其一。

安裝

npm i postcss postcss-loader -D

安裝其中的某個插件比如 Autoprefixer

npm i autoprefixer -D
module.exports = merge(common, {
  //...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(scss|sass)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
    ]
  },
  //...
});

接下來我 執(zhí)行 npm run build 打包,
這里有兩個警告(不影響程序的)

  1. .babelrc 中需要croejs你需要指定 croejs的版本 https://blog.csdn.net/qq_41893551/article/details/90109391
  1. PostCSS中http://www.itdecent.cn/p/15d51e796dca
    autoprefixer版本高了,引用要修改,需要用新的方法

解決這些之后,就可以快樂的 npm run build

之后我們來配置 webpack.dev.config.js

const merge  = require('webpack-merge');
const common = require('./webpack.common.config')
const path = require('path')

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

module.exports = merge( common, {
    mode:"development",
    output:{
        filename:"js/[name].[hash:4].bundle.js",
    },
    devServer: {
       //ajax請求的路徑,一般可以做mock數(shù)據(jù)用
        contentBase: path.resolve(__dirname, '../public'),
        // 熱更新
        open: true, 
        // 端口
        port: 8080,
        historyApifFallback:true,
        // 單頁面應(yīng)用切換路由不丟失
        compress: true,
        hot: true
    },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    "postcss-loader"
                ]
            },
            {
                test:/\.less$/,
                use:[
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    "postcss-loader",
                    "less-loader"
                ]
            },
            {
                test:/\.(sass|scss)/,
                use:[
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    "postcss-loader",
                    "sass-loader"
                ]
            }
        ]
    },
    plugins:[
        //hash選項(xiàng)的作用是 給生成的 jshash 值,該 hash 值是該次 webpack 編譯的 hash 值。默認(rèn)值為 false
        new HtmlWebpackPlugin({
            template:"public/index.html",
            inject: 'body',
            // hash: false
        }),
        new MiniCssExtractPlugin({
              // 這里我們注意一下 在開發(fā)環(huán)境中 我們不加上hash
              // 在生產(chǎn)環(huán)境 我們使用hash
              filename: 'css/[name].css',
        chunkFilename: 'css/[id].css',
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
})

開發(fā)環(huán)境打包壓縮之類的就不需要了,那么css引入還是有兩鐘方式:

  1. 繼續(xù)以文件方式MiniCssExtractPlugin中將hash去掉
  2. 以style引入那么就不要用MiniCssExtractPlugin引入css,改用style-loader引入css

到這里我們已經(jīng)實(shí)現(xiàn)了一個簡單的react腳手架,不過我還需要進(jìn)一步優(yōu)化

添加圖片的loader

file-loader 可以對圖片文件進(jìn)行打包,但是 url-loader 可以實(shí)現(xiàn) file-loader 的所有功能,且能在圖片大小限制范圍內(nèi)打包成base64圖片插入到j(luò)s文件中,這樣做的好處是什么呢?先一步一步走著!

安裝url-loader

這里需要注意,url-loader依賴于file-loader,所有我們兩個loder都要安裝

npm i file-loader url-loader -D //npm install file-loader url-loader --save-dev

在app.js引入圖片

import React from 'react';
import './app.scss';
import background from './asset/img/background.png';

function App() {
  return (
    <div className="app">
      <h1 className="text">Hello Webpack</h1>
      <img className="background" src={background} alt=""/>
    </div>
  );
}

export default App;

并且使用一張css引入

.App {
    height: 400px;
    display: flex;
    justify-content: center;
    align-items: center;
    div{
        width: 100%;
        height: 200px;
        background:url('./assets/img/background.jpg');
    }
    img{
        height: 200px;
    }
    h1 {
        font-size: 16px;
        color: #fff;
    }
}

執(zhí)行 npm run build ,你會發(fā)現(xiàn)css的圖片沒有,

在打開index.js之后你會發(fā)現(xiàn)你的圖片路徑是images/background.jpg,很明顯你的圖片路徑不正確,因?yàn)槟愕腸ss和index.html是不同級的index.html在dist的下面,而css在dist/css下載
在webpack.prod.config.js中有處理圖片的loader你可以修改options

  options: {
    // options中的[name].[ext]表示,輸出的文件名為 原來的文件名.后綴 ;
    name: '[name].[ext]',
    // outputPath是輸出到dist目錄下的路徑,即dist/images/...  ;
    outputPath: 'images/',
    // limit表示,如果你這個圖片文件大于8192b,即8kb,轉(zhuǎn)而去使用file-loader,
    // 減少了http請求,但是如果文件過大,js文件也會過大,得不償失,這是為什么有l(wèi)imit的原因!
    limit: 8192,
    // publicPath設(shè)置圖片片引入路徑
+   publicPath: '../images/'
},

修改完之后 npm run build
聰明的同學(xué)你一定會反應(yīng)過來index.html的圖片路徑是不對的
可以修改webpack.prod.config.js中sass css less 將其MiniCssExtractPlugin改成options配置參數(shù)

{
  test: /\.(sass|scss)/,
  use: [
    {
      loader:MiniCssExtractPlugin.loader,
      options: {
          publicPath: '../'
      }
    },
    "css-loader",
    "postcss-loader",
    "sass-loader"
  ]
},

同理 webpack.dev.config.js 也需要這樣配置,
但是如果我們 在development中使用的是 style-loader那么我們的不需要配置,以在style標(biāo)簽里,路徑和index.html中是一樣的
解決博客https://blog.csdn.net/a806488840/article/details/80920291?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

添加字體圖標(biāo)loader

字體圖標(biāo)需要我們之前已經(jīng)安裝過的 file-loader
可以在package.json中j檢查確定有沒有安裝
圖標(biāo)就不細(xì)說了,因?yàn)榇蟛糠忠箜?xiàng)目不一樣

在webpack.common.config.js里配置

module: {
    rules: [
      //...
      {
        test: /\.(eot|ttf|svg|woff|woff2)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'font/'
          }
        }
      }
    ]
  }

最后我們還需要一個報(bào)錯的配置 source-map

這個不需要我們下載,直接在webpack.common.config.js配置

module.exports = {
    devtool:"cheap-module-eval-source-map",
    entry...
    // 現(xiàn)在的webpack高級版本應(yīng)該不需要這個配置瀏覽器也會報(bào)錯
    // 那我為什么還要寫呢?畢竟還有用較低版本的同學(xué)。。。
}

這些基本的配置完畢之后我還可以配置一些其他東西

process.env

這里的process.env就是Nodejs提供的一個API,
可以直接在webpack.conmmon.config.js中 輸出這個對象

const path = require('path')
console.log( process,'process-------')
module.exports = {
    devtool:"cheap-module-eval-source-map",
    entry...
// 由于這個對象包含的東西太多就不展示了,我們可以看到里面有一個env

在這個env中我們可以這樣配置

let TARGET = process.env.npm_lifecycle_event; // 是build,還是start
let isDev  = process.env.NODE_ENV // 判斷是production 還是 development 
console.log(TARGET,isDev)

不過在這之前我們還在要package.json中配置,可以理解為傳遞參數(shù)
在不同的系統(tǒng)比如window環(huán)境變量的命令也有 不同 可以用 cross-env 統(tǒng)一

npm i --save-dev cross-env

{
  ...
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
  }
}

這樣我們可以通過 isDev 這個變量去控制我們的參數(shù)

  module.exports = {
   mode: isDev === 'production' ? 'production' : 'development'
}

還可以配置resolve

resolve: {
    extensions: ['.js', '.jsx', '.json', '.less', '.scss'],
    modules: [ path.resolve(__dirname, 'src'), 'node_modules' ],
    alias: {
      _components: path.join(__dirname, '../src/components'),
      _images: path.join(__dirname, '../src/images'),
      _pages: path.join(__dirname, '../src/pages'),
      _font: path.join(__dirname, '../src/font'),
      _util: path.join(__dirname, '../src/util'),
      _mock: path.join(__dirname, '../src/mock'),
    }
  },
  // 我們平時引入文件時 通過 import from 引入一個文件 例:import Home from "../src/pages/Home"
  // alias的作用就是 import Home from "_pages/Home" 這樣就可以方便快捷的找到Home 并且通過 path 所以我們不用擔(dān) // 心文件路徑不對的問題,因?yàn)槲覀兪褂玫氖墙^對路徑

  // modules 去哪里尋找第三方模塊 默認(rèn)就是node_modules 簡單來說你有許多模塊都是在 components文件 中導(dǎo)入
  // 原本你可能需要這樣一個很長的路徑 import '../../../components/button
  // 設(shè)置好 modules 之后 你只需要import 'button'

https://www.cnblogs.com/joyco773/p/9049760.html

現(xiàn)在我們的基本配置就這些了 !!!

當(dāng)然只有這些我們的項(xiàng)目還不夠豐滿,

后續(xù)我將會介紹如何在我們自己的"腳手架"中添加更多的模塊

比如 antd UI 或者是 Mobx 再或者我們將使用ts來完成我們的項(xiàng)目

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容