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

webpack+react

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

建一個空文件夾 webpack-react

  • 命令行執(zhí)行
    npm init -y
    // 生成一個package.json 
    // npm install就是根據(jù)這個文件去下載我們項目里使用的包
    // 也就是配置項目所需的運行和開發(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 配置是標準的 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配置文件的路徑 默認是根目錄文件下的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(默認)/ 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文件紊遵,更多配置點我
    • 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 文件中的入口進行修改,因為我們現(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)在嘗試一下重新運行 npm run build 炬丸,會發(fā)現(xiàn)打包失敗了瘫寝,為什么呢?接著看.....

因為webpack根本識別不了jsx語法稠炬,那怎么辦焕阿?使用loader對文件進行預(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"外厂,但最好在當前根目錄冕象,注意,一定要是根目錄V=グ纭! 新建一個配置文件 .babelrc 配置相關(guān)的"presets":

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          // 大于相關(guān)瀏覽器版本無需用到 preset-env
          "edge": 17,
          "firefox": 60,
          "chrome": 67,
          "safari": 11.1
        },
        // 根據(jù)代碼邏輯中用到的 ES6+語法進行方法的導(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

我們再確認一次我們的目錄:

  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è)務(wù)代碼更新,而頁面卻沒變化的問題幔亥,你想想看耻讽,假如客戶端請求js文件的時候發(fā)現(xiàn)名字是一樣的,那么它很有可能不發(fā)新的數(shù)據(jù)包帕棉,而直接用之前緩存的文件针肥,當然,這和緩存策略有關(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里因為js文件名字不同已經(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 轻绞,那我們把這部分不變的代碼單獨打包采记。

修改 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,沒有變過奸远。
這是因為我們還沒有抽離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)鍵的一個值,他可以是一個字符串佩番,也可以是正則表達式众旗,還可以是函數(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é)郭脂,當然,隨著業(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 啟動7杓妗H欢簟!

現(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)報錯了,wbpack只能編譯js文件,css文件是無法被識別并編譯的,我們需要loader加載器來進行預(yù)處理吧彪。 首先安裝 style-loader 和 css-loader :

npm i style-loader css-loader -D

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

打包出CSS獨立文件

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

webpack 4.0以后,官方推薦使用extract-text-webpack-plugin插件來打包css文件瞄摊⊙郑可以自己了解

安裝

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文件,更多配置點我
                removeComments:去除注釋
                collapseWhitespace:去除空格
        */
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: "public/index.html",
            inject: "body",
            minify: {
                removeComments: true,
                collapseWhitespace: true,
            }
        }),
        new CleanWebpackPlugin({
            // 項目里有改動才會有新的包 打包編譯前清理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({
                // 正則表達式蹲嚣,用于匹配需要優(yōu)化或者壓縮的資源名。默認值是/.css$/g  
                assetNameRegExp:/\.css$/g,
                // 用于壓縮和優(yōu)化CSS 的處理器祟牲,默認是 cssnano.
                cssProcessor:require("cssnano"),
                //  傳遞給cssProcessor的插件選項隙畜,默認為{}
                cssProcessorPluginOptions:{
                //  預(yù)設(shè): '默認',         丟棄注釋:     刪除全部注釋 
                preset:['default', { discardComments: { removeAll:true } }]
                },
                //表示插件能夠在console中打印信息说贝,默認值是true
                canPrint:true,
            }),
        ],
    }
})
    

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

Sass and Less

我們寫項目的時候沒幾個人會去寫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進行補全Postcss

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

  1. 使用下一代css語法
  2. 自動補全瀏覽器前綴
  3. 自動把px代為轉(zhuǎn)換成rem
  4. css 代碼壓縮等等
    postcss 只是一個工具躁愿,本身不會對css一頓操作叛本,它通過插件實現(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.reibang.com/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選項的作用是 給生成的 jshash 值,該 hash 值是該次 webpack 編譯的 hash 值逸雹。默認值為 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)實現(xiàn)了一個簡單的react腳手架,不過我還需要進一步優(yōu)化

添加圖片的loader

file-loader 可以對圖片文件進行打包营搅,但是 url-loader 可以實現(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,很明顯你的圖片路徑不正確,因為你的css和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標簽里,路徑和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

添加字體圖標loader

字體圖標需要我們之前已經(jīng)安裝過的 file-loader
可以在package.json中j檢查確定有沒有安裝
圖標就不細說了,因為大部分要求項目不一樣

在webpack.common.config.js里配置

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

最后我們還需要一個報錯的配置 source-map

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

module.exports = {
    devtool:"cheap-module-eval-source-map",
    entry...
    // 現(xiàn)在的webpack高級版本應(yīng)該不需要這個配置瀏覽器也會報錯
    // 那我為什么還要寫呢敦冬?畢竟還有用較低版本的同學(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 所以我們不用擔 // 心文件路徑不對的問題介蛉,因為我們使用的是絕對路徑

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

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

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

當然只有這些我們的項目還不夠豐滿,

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萌庆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子币旧,更是在濱河造成了極大的恐慌践险,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巍虫,居然都是意外死亡彭则,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門占遥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俯抖,“玉大人,你說我怎么就攤上這事瓦胎》移迹” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵搔啊,是天一觀的道長柬祠。 經(jīng)常有香客問我,道長负芋,這世上最難降的妖魔是什么漫蛔? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮示罗,結(jié)果婚禮上惩猫,老公的妹妹穿的比我還像新娘。我一直安慰自己蚜点,他們只是感情好轧房,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绍绘,像睡著了一般奶镶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陪拘,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天厂镇,我揣著相機與錄音,去河邊找鬼左刽。 笑死捺信,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的欠痴。 我是一名探鬼主播迄靠,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喇辽!你這毒婦竟也來了掌挚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤菩咨,失蹤者是張志新(化名)和其女友劉穎吠式,沒想到半個月后陡厘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡特占,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年糙置,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摩钙。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡罢低,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胖笛,到底是詐尸還是另有隱情网持,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布长踊,位于F島的核電站功舀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏身弊。R本人自食惡果不足惜辟汰,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阱佛。 院中可真熱鬧帖汞,春花似錦、人聲如沸凑术。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淮逊。三九已至催首,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泄鹏,已是汗流浹背郎任。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留备籽,地道東北人舶治。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像车猬,于是被迫代替她去往敵國和親霉猛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容