搭建webpack項目框架


  • 隨着業務發展和前端人員的增加,搭建一個通用框架以及制定統一規范就成了必然。對於選型這方面,一開始好像就沒考慮其他框架,直接選了webpack。webpack的優點就不多說了,可擴展性,強大的npm插件庫,說干就干。
  • 基於公司基礎以及業務限制,一口吃不成個胖子,沒辦法做成最理想的狀態,也就是一份配置文件,npm build可以直接打包所有項目。也許未來會是這個狀態,但目前來看,這種一勞永逸的方案並不是我們公司最合適的方案。一方面公司項目機制並不成熟,老項目也不少,直接打包所有項目很有可能影響老項目;另一方面打包所有項目權限太大,一個人犯錯,可能導致公司所有項目都出問題,所以還是自己負責自己的項目就好。現在實現的效果也還不錯,簡簡單單3句命令,就實現了本地、測試和線上環境的區分和打包。

 

命令實例如下(項目名稱是testDemo):
npm run start app/testDemo
//dev 環境
npm run test app/testDemo
//test 環境
npm run build app/testDemo
//prod 環境


二、規划

現在就說下具體的規划吧,想法其實也很簡單:

1、新建2個同級目錄,一個是webpack(項目源目錄),一個是 build(打包之后的項目的目錄);

2、通過運行不同的命令(主要是命令最后面的項目名稱不一樣),將項目從webpack打包到 build 里,並且webpack和build的目錄結構一模一樣,比如上面實例中的項目testDemo,它的源目錄結構是webpack/app/testDemo,那打包之后的路徑就是build/app/testDemo,這樣的結構更易於操作和后期維護。

 

三、結構

先來看看webpack的目錄結構吧:

 

node_modules common app store .gitignore package.json webpack.common.js webpack.dev.js webpack.prod.js webpack.test.js package-lock.json

 

說明:

node_modules:安裝好的包。
common:公共目錄,比如里面可以放公共css、公共插件等。
app、store:項目目錄,和 build 內的目錄保持一致。
.gitignore:需要忽略的東西,比如 node_modules 等。
package.json:配置文件,如果下載了新的 loader,package.json 文件會有變動。
webpack.common.js:webpack 的公共配置文件。
webpack.dev.js:本地開發配置文件。
webpack.prod.js:線上環境配置文件。
webpack.test.js:測試環境配置文件。
package-lock.json:npm init 過程中生成的 json 文件,無需關心。

 

 

 

四、問題

那么現在面臨的就有以下幾個問題了:

1、我想要打包哪個文件就可以打包哪個文件,並且打包到指定目錄,這個是在哪配置的,應該如何配置?

2、配置文件里都有入口配置和出口配置,並且如果給對應的 html 對應的加上他想要的主 js 文件,我知道這個肯定是動態獲取和配置,但具體應該怎么實現?

3、css、js、html、img 是怎么處理的,用到了哪些 loader?

4、我知道的配置文件只有一個,為什么我的會有4個:webpack.common.js、webpack.dev.js、webpack.test.js、webpack.prod.js,為什么要寫這么多配置文件,以及是怎么實現的?

5、打包一次就會生成一個目錄和一批文件,而且后綴名還不一樣,久而久之文件夾豈不是越來越大,怎么解決這個問題,原理又是什么?

6、無論你有沒有修改文件,只要打包一次,webpack 就會重新運行一遍,並且生成不同的文件名,有沒有什么辦法避免這種情況,至少未修改的文件就不會再被打包一遍?

7、如何提取公共模塊?比如 index1.js 和 index2.js 都引用了 jQuery,有沒有什么辦法,可以讓打包后的 jQuery 只有一份?

8、本地環境我並不想壓縮代碼,因為壓縮會很慢,測試和線上環境才會去壓縮,這個該怎么實現,需要用什么 loader?

9、打包之后的 css 我並不像被壓縮在 js 一塊,想單獨放一個 css 文件夾里,這個可以實現嗎,怎么去實現?

10、css 是怎么實現的壓縮,和 js 壓縮一樣嗎,需要注意什么?

11、如何給靜態資源配置域名地址,而本地不需要,這個怎么實現?

12、package.json 里的 devDependencies 和 dependencies 有什么區別,需要注意什么?

13、.gitignore 的作用,以及如何配置?

下面一一來解答:

1、想要打包哪個文件就可以打包哪個文件,也就是上面提到的 npm start app/testDemo,運行這個命令,就會打包 webpack 下 app/testDemo 這個目錄,想要實現這個,只需配置 package.json 和 webpack.common.js 即可:

package.json(不懂的可以去了解下 scripts)

"scripts": {
    "watch": "webpack --progress --watch",
    "start": "webpack-dev-server --open --config webpack.dev.js --env.project",
    "test": "webpack --config webpack.test.js --env.project",
    "build": "webpack --config webpack.prod.js --env.project"
}
webpack.common.js

module.exports = function (env) {
    let dirpath = "";
    if (env.project) {
        dirpath = env.project;
    }
}
這樣就實現了想要打包哪個文件就可以打包哪個文件,至於打包到哪個文件,就需要自己手動寫一些配置了。

主要配置好 output 即可:

const publicPath = '../build/' + dirpath + ''; //打包之后的路徑:build,而 dirpath 是上面獲取到的值

const webpackConfig = {
    output: {
        filename: 'js/[name].[chunkhash:8].js',
        chunkFilename: 'js/[name].[chunkhash:8].js',
        path: path.resolve(__dirname, publicPath),
        publicPath: "/"
    }
}

 

 

2、單頁面文件比較簡單,比如 index.html ,需要引入 index.js 打包后生成的 js 文件,直接 script 標簽引入即可,但手動的方法不方便且易出錯,怎么實現 index.html 自動引入 index.js 打包后的文件呢?這時候需要用到一個 loader,即:html-webpack-plugin,具體實現方法也很簡單:

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

webpackConfig.entry = {
    index: "./js/index.js"
}

webpackConfig.plugins = [
    new webpack.optimize.CommonsChunkPlugin({ //提取公共模塊,比如index1.js和index2.js都引入了jquery,那么jquery就會被當作公共文件被打包進runtime
        name: ['runtime'],
        minChunks: Infinity
    }),
    new HtmlWebpackPlugin({
        template: 'index.html', //原html文件
        filename: 'index.html', //index.html 生成后的文件名稱
        chunks: ["index", 'runtime'] //需要的chunks
    })
]

 

但是如果出現多頁面,並且有很多很多項目,不可能每個項目都這樣一步一步去配置,這時候就需要動態獲取 entry、動態加載 HtmlWebpackPlugin。

//動態獲取entry和動態加載HtmlWebpackPlugin
//js文件夾下的文件都會加進去並且被相應的html引用,所以不需要加進去的js文件一定不要放在js文件夾下面,可以新建一個文件夾去放,比如common/meta.js
const entry = {};
let fs = require('fs');//無需安裝,直接使用
let commonEntryAndHtml = function (fileUrl) {
	try {
		let files = fs.readdirSync(fileUrl);//讀取該文件夾
		files.forEach(function (file) {
			if (file == "js") {
				let jsFile = fs.readdirSync(dirpath + "/" + file);
				jsFile.forEach(function (jsFile) {
					let jsFileName = jsFile.split(".")[0];
					entry[jsFileName] = './' + dirpath + '/js/' + jsFile;
				})
			}
			if (file.match(/\.(html|php)$/)) {
				var htmlFilename = file.split(".")[0];
				const plugin = new HtmlWebpackPlugin({
					template: '' + dirpath + '/' + file + '', //原html文件
					filename: '' + file + '', //index.html 生成后的 文件名稱
					chunks: ["" + htmlFilename + "", 'runtime'] //需要的chunks
				})
				webpackConfig.plugins.push(plugin);
			}
		});
	} catch (e) {
		console.log(e);
	}
};
commonEntryAndHtml(dirpath);
webpackConfig.entry = entry; //添加entry

  

不過動態加載是有要求和前提的,對文件目錄結構以及命名有一定的要求的規范,不是你想怎么寫都可以打包成功。

我這里的規范以 testDemo 為例:

  • 項目的目錄結構和 testDemo保持一致:html文件在最外層,js、css、json、img單獨文件夾。
  • js 目錄純粹化:由於 webpack.common.js 里是動態獲取 entry 和動態加載 HtmlWebpackPlugin,所以 js 文件夾下的文件都會加進去並且被相應的 html 引用,所以不需要加進去的 js 文件一定不要放在 js 文件夾下面,可以新建一個文件夾去放,比如 common/meta.js。
  • html 和 js 的文件名保持一致:html 文件需要引入的入口 js 文件名必須和 html 的文件名 必須保持一致,比如 index.html 對應的 js 就是 index.js,edit.html 對應的 js 就是 edit.js,這樣配置文件才會知道哪個 html 文件需要加載什么 js 文件。

 

3、css、js、html、img 要用到哪些 loader,用過 webpack 的其實都應該比較熟練了,我就直接貼代碼吧。

rules: [{
    test: /\.(html|php)$/,
    loader: 'html-loader' //處理html中的圖片
}, {
    test: /\.(js)$/,
    exclude: /(node_modules)/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: ['env']
        }
    }
},
{
    test: /\.(css|less)$/,
    use: ExtractTextPlugin.extract({
        fallback: "style-loader",
        use: [{
            loader: "css-loader"
        },{
            loader: "less-loader"
        }]
    })
},
{
    test: /\.(png|svg|jpg|gif|svga)$/,
    use: [{
        loader: 'url-loader?limit=1&name=img/[name].[hash:8].[ext]'
    }]
}
]

 

需要稍微注意一點的是處理圖片的 loader 以及圖片的引用方式。

url-loader 中的 limit = 1,代表大小小於 1kb 的圖片地址會被轉化成 base64 的 url;html-loader 是為了處理 html 中的圖片地址;js 中的圖片需要通過 require 方式進行引用,直接引用無效。

(1)HTML

<img src="./img/1.jpg">2)CSS

.img-box{
    background: url("../img/1.jpg") no-repeat;
    background-size: 100% 100%;
    width: 100px;
    height: 100px;
}
(3)JS

const imgUrl = require('./img/1.jpg');
$("body").append(`<img src="${imgUrl}">`);

 

 

4、配置文件文件分為這4個,其實是為了更好的區分環境(本地、測試、線上)以及維護配置代碼。

  • webpack.common.js 是公共配置文件,里面是本地、測試和線上都需要的配置,包括動態入口和出口、處理html、css、js、圖片等需要的 loader、提取公共文件、配置別名等;
  • webpack.dev.js 是本地環境配置文件,里面只需要配置 publicPath、監聽代碼變化自動提交並刷新網頁即可;
  • webpack.test.js 是測試環境配置文件,里面也需要配置 publicPath,但測試環境的 publicPath 和本地的 publicPath 不一樣,我們約定的是測試環境的域名和路徑,測試環境也需要加上清除文件夾的操作,防止每次 webpack 導致文件過大,還有壓縮文件的操作,包括 js 和 css 的壓縮,同時也會配置 test 的環境變量;
  • webpack.prod.js 就是線上環境配置文件,它和測試環境的配置文件幾乎一模一樣,唯獨 publicPath 是線上環境的域名和路徑。

而 npm start app/testDemo、npm test app/testDemo、npm run build app/testDemo 就是根據環境不同,執行不同的配置文件來達到不同的效果。

具體的實現到時候直接貼代碼,不同環境的配置文件如何引用公共配置文件可以稍微說一下,主要通過 webpack-merge 。

比如如下是 webpack.dev.js:

const merge = require('webpack-merge');


module.exports = function(env){
    const common = require('./webpack.common.js')(env);

    const commonPath = common.output.path;
    const dirpath = commonPath.substring(commonPath.lastIndexOf("build"));
    const dirpaths = dirpath.replace(/\\/g, "/");
    const publicPath = "./../" + dirpaths + "/"; //開發環境的路徑

    return merge(common, {
        devtool: 'inline-source-map',
        devServer: {
            contentBase: publicPath //監聽代碼變化自動提交並刷新網頁
        }
    })
};

 

 

5、為了解決文件越來越大的問題,只需要每次在打包之前,將原來目錄里的文件清除掉即可。

const fs = require('fs');//無需安裝,直接使用

let emptyDir = function (fileUrl) {
    try {
        let files = fs.readdirSync(fileUrl);//讀取該文件夾
        files.forEach(function (file) {
            let stats = fs.statSync(fileUrl + '/' + file);
            if (stats.isDirectory()) {
                emptyDir(fileUrl + '/' + file);
            } else {
                fs.unlinkSync(fileUrl + '/' + file);
                console.log("刪除文件" + fileUrl + '/' + file + "成功");
            }
        });
    } catch (e) {
        console.log(e);
    }
};
emptyDir(commonPath);
 

 

6、為了避免未修改的文件被打包,webpack 本身自帶一個插件去處理:

new webpack.HashedModuleIdsPlugin()

 

 

7、提取公共模塊,webpack 也自帶了一個插件去處理:

new webpack.optimize.CommonsChunkPlugin({ //最小化入口 chunk
    name: ['runtime'],
    minChunks: Infinity
})

 

 

8、本地不需要壓縮,測試和線上環境才去壓縮,之前的環境划分就派上了很好的用場了,只需要在 webpack.test.js 和 webpack.prod.js 中配置壓縮即可。至於用到什么 loader,js 一般用的是 uglifyjs-webpack-plugin,css 用的是 optimize-css-assets-webpack-plugin。配置如下:

uglify 里面的配置可不寫,我這里主要寫的是 ie8 的兼容,sourceMap 也可設置為 true,這樣利於調試。

const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

new UglifyJSPlugin({
    output: {
        quote_keys: true,
        keep_quoted_props: true,
        screw_ie8: false
    },
    mangleProperties: {
        screw_ie8: false
    },
    compress: {
        properties: false,
        screw_ie8: false
    },
    mangle: {
        screw_ie8: false,
        except: ['e']
    },
    sourceMap: false
})

 

 optimize-css-assets-webpack-plugin 能使 css 最小化,如果不做處理的話,壓縮后會導致一些兼容的前綴丟失,所以里面添加了一些配置,方式壓縮過狠。

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

new OptimizeCssAssetsPlugin({
    assetNameRegExp: /\.css\.*(?!.*map)/g, //注意不要寫成 /\.css$/g
    cssProcessor: require('cssnano'),
    cssProcessorOptions: {
        discardComments: { removeAll: true },
        // cssnano 集成了autoprefixer的功能
        // 會使用到autoprefixer進行無關前綴的清理
        // 關閉autoprefixer功能
        // 使用postcss的autoprefixer功能
        autoprefixer: false
    },
    canPrint: true
})

 

 

9、如果不做任何處理的話,打包后的 css 會被壓縮在 js 之中,第一不太美觀,第二會導致 js 文件過大,所以有必要將 css 分離出來,這時候要用到一個 extract-text-webpack-plugin 的插件。配置如下:

const ExtractTextPlugin = require('extract-text-webpack-plugin');

//loader
{
    test: /\.(css|less)$/,
    use: ExtractTextPlugin.extract({
        fallback: "style-loader",
        use: [{
            loader: "css-loader"
        },{
            loader: "less-loader"
        }]
    })
}

//plugin
new ExtractTextPlugin('css/[name].[contenthash:8].css')

 

 

10、前面已經說過了,css 壓縮采用  optimize-css-assets-webpack-plugin 插件,js 壓縮采用的是 uglify,要注意的就是如果不做任何配置,css 里有些做兼容的前綴,比如 -webkit- 等就會被當作沒用的東西被壓縮掉,再加一些配置就可以了。

 

11、給靜態資源配置不同的地址,無論是本地還是測試還是線上,都是通過 publicPath 進行實現的。

本地:

const common = require('./webpack.common.js')(env);
const commonPath = common.output.path;
const dirpath = commonPath.substring(commonPath.lastIndexOf("build"));
const dirpaths = dirpath.replace(/\\/g, "/");
const publicPath = "./../" + dirpaths + "/"; //開發環境的路徑

 

測試(前半部分代碼和本地保持一致):

const publicPath = "https://test.jojo.com/" + dirpaths + "/"; //測試環境js和css的路徑

 

線上(前半部分代碼和本地保持一致):

const publicPath = "https://www.jojo.com/" + dirpaths + "/"; //線上環境js和css的路徑

 

 

12、以前一直在糾結一個npm安裝的包依賴管理的問題。是這樣的:我們在使用 npm install 安裝模塊或插件的時候,有兩種命令把他們寫入到 package.json 文件里面去,他們是:--save-dev 或 --save。

 

首先需要說明的是Dependencies一詞的中文意思是依賴和附屬的意思,而dev則是 develop(開發)的簡寫。

 

所以它們的區別在 package.json 文件里面體現出來的就是,使用 --save-dev 安裝的 插件,被寫入到 devDependencies 域里面去,而使用 --save 安裝的插件,則是被寫入到 dependencies 區塊里面去。

 

那 package.json 文件里面的 devDependencies  和 dependencies 對象有什么區別呢?

 

devDependencies  里面的插件只用於開發環境,不用於生產環境,而 dependencies  是需要發布到生產環境的。

 

比如我們寫一個項目要依賴於jQuery,沒有這個包的依賴運行就會報錯,這時候就把這個依賴寫入dependencies ;

而我們使用的一些構建工具比如glup、webpack這些只是在開發中使用的包,上線以后就和他們沒關系了,所以將它寫入devDependencies。

 

13、.gitignore 就是將不想上傳的文件或文件夾上傳到倉庫里,比如 npm install 生成的巨大無比的 node_modules。使用方法參考:https://git-scm.com/docs/gitignore

 

五、代碼

差不多就介紹到這吧,下面直接把代碼貼出來,可以直接吧把 package.json 引到自己的文件中,然后 npm install 直接安裝相關依賴。當然也可以自己 npm init 后一個一個下載安裝,自己喜歡就好。

補充一些相關命令:

  • 安裝淘寶鏡像:
npm install -g cnpm --registry=https://registry.npm.taobao.org

 

  • 刪除 node_modules :
npm uninstall 

 

  • 安裝loader(比如安裝 html-loader 和 vue):
cnpm install --save-dev html-loader
cnpm install --save vue
 

 

package.json

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "watch": "webpack --progress --watch",
    "start": "webpack-dev-server --open --config webpack.dev.js --env.project",
    "test": "webpack --config webpack.test.js --env.project",
    "build": "webpack --config webpack.prod.js --env.project"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^8.6.5",
    "babel": "^6.23.0",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-plugin-transform-es3-property-literals": "^6.22.0",
    "babel-preset-env": "^1.6.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "babel-runtime": "^6.26.0",
    "brace-expansion": "^1.1.8",
    "chokidar": "^2.0.0",
    "clean-webpack-plugin": "^0.1.17",
    "compression": "^1.7.1",
    "connect-history-api-fallback": "^1.5.0",
    "css-loader": "^0.28.7",
    "del": "^3.0.0",
    "emilia": "^0.1.1",
    "enhanced-resolve": "^4.0.0-beta.4",
    "es3ify-loader": "^0.2.0",
    "express": "^4.16.2",
    "extract-text-webpack-plugin": "^3.0.1",
    "fastclick": "^1.0.6",
    "file-loader": "^0.11.2",
    "fs.realpath": "^1.0.0",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^2.30.1",
    "html-withimg-loader": "^0.1.16",
    "http-proxy-middleware": "^0.17.4",
    "install": "^0.10.1",
    "internal-ip": "^3.0.1",
    "jquery": "^1.11.3",
    "jquery-lazyload": "^1.9.7",
    "lazyload": "^2.0.0-beta.2",
    "lazysizes": "^4.0.0-rc4",
    "less": "^2.7.3",
    "less-loader": "^4.0.5",
    "markdown-loader": "^3.0.0",
    "npm": "^5.4.2",
    "opn": "^5.2.0",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "php-loader": "^0.2.0",
    "php-parser": "^3.0.0-alpha2",
    "post-loader": "^2.0.0",
    "postcss-loader": "^2.1.6",
    "selfsigned": "^1.10.1",
    "source-list-map": "^2.0.0",
    "style-loader": "^0.18.2",
    "tapable": "^1.0.0-beta.5",
    "uglifyjs-webpack-plugin": "^0.4.6",
    "url-loader": "^1.0.1",
    "utils": "^0.3.1",
    "webpack": "^3.10.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0",
    "webpack-php-loader": "^0.5.0",
    "webpack-remove-hashed-files": "^1.2.1",
    "webpack-spritesmith": "^0.3.3",
    "zepto": "^1.2.0"
  },
  "dependencies": {
    "svgaplayerweb": "^2.1.0",
    "vue": "^2.5.16",
    "vue-loader": "^15.2.4",
    "vue-resource": "^1.5.1",
    "vue-router": "^3.0.1",
    "vue-template-compiler": "^2.5.16"
  }
}

 

 

webpack.common.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');

module.exports = function (env) {
    let dirpath = "";
    if (env.project) {
        dirpath = env.project;
    }
    const publicPath = '../build/' + dirpath + ''; //打包之后的路徑

    const webpackConfig = {
        output: {
            filename: 'js/[name].[chunkhash:8].js',
            chunkFilename: 'js/[name].[chunkhash:8].js',
            path: path.resolve(__dirname, publicPath),
            publicPath: "/"
        },
        resolve: {
            alias: {
                '@base': path.resolve(__dirname, "common")
            }
        },
        module: {
            rules: [{
                test: /\.(html|php)$/,
                loader: 'html-loader' //處理圖片
            }, {
                test: /\.(js)$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['env']
                    }
                }
            },
            {
                test: /\.(css|less)$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: [{
                        loader: "css-loader"
                    },{
                        loader: "less-loader"
                    }]
                })
            },
            {
                test: /\.(png|svg|jpg|gif|svga)$/,
                use: [{
                    loader: 'url-loader?limit=1&name=img/[name].[hash:8].[ext]'
                }]
            }
            ]
        },
        plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                Vue: ['vue/dist/vue.min.js'],
                FastClick: 'fastclick',
                marked: 'marked',
                SVGA: "svgaplayerweb"
            }),
            new ExtractTextPlugin('css/[name].[contenthash:8].css'),
            new webpack.HashedModuleIdsPlugin(),
            new webpack.optimize.CommonsChunkPlugin({ //提取公共模塊,比如index1.js和index2.js都引入了jquery,那么jquery就會被當作公共文件被打包進runtime
                name: ['runtime'],
                minChunks: Infinity
            })

        ]
    };

 

    //動態獲取entry和動態加載HtmlWebpackPlugin
    //js文件夾下的文件都會加進去並且被相應的html引用,所以不需要加進去的js文件一定不要放在js文件夾下面,可以新建一個文件夾去放,比如common/meta.js
    const entry = {};
    let fs = require('fs');//無需安裝,直接使用
    let emptyDir = function (fileUrl) {
        try {
            let files = fs.readdirSync(fileUrl);//讀取該文件夾
            files.forEach(function (file) {
                if (file == "js") {
                    let jsFile = fs.readdirSync(dirpath + "/" + file);
                    jsFile.forEach(function (jsFile) {
                        let jsFileName = jsFile.split(".")[0];
                        entry[jsFileName] = './' + dirpath + '/js/' + jsFile;
                    })
                }
                if (file.match(/\.(html|php)$/)) {
                    var htmlFilename = file.split(".")[0];
                    const plugin = new HtmlWebpackPlugin({
                        template: '' + dirpath + '/' + file + '', //原html文件
                        filename: '' + file + '', //index.html 生成后的 文件名稱
                        chunks: ["" + htmlFilename + "", 'runtime'] //需要的chunks
                    })
                    webpackConfig.plugins.push(plugin);
                }
            });
        } catch (e) {
            console.log(e);
        }
    };
    emptyDir(dirpath);
    webpackConfig.entry = entry; //添加entry
    return webpackConfig;
};

 

 

 

webpack.dev.js

const merge = require('webpack-merge');


module.exports = function(env){
    const common = require('./webpack.common.js')(env);

    const commonPath = common.output.path;
    const dirpath = commonPath.substring(commonPath.lastIndexOf("build"));
    const dirpaths = dirpath.replace(/\\/g, "/");
    const publicPath = "./../" + dirpaths + "/"; //開發環境的路徑

    return merge(common, {
        devtool: 'inline-source-map',
        devServer: {
            contentBase: publicPath //監聽代碼變化自動提交並刷新網頁
        }
    })
};

 

 

webpack.test.js

const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); //壓縮css
const fs = require('fs');//無需安裝,直接使用

module.exports = function (env) {
    const common = require('./webpack.common.js')(env);
    const commonPath = common.output.path;
    const dirpath = commonPath.substring(commonPath.lastIndexOf("build"));
    const dirpathss = dirpaths.replace(/\\/g, "/");
    const publicPath = "https://test.jojo.com/" + dirpathss + "/"; //測試環境js和css的路徑

    //清除文件夾,防止每次webpack導致文件過大
    let emptyDir = function (fileUrl) {
        try {
            let files = fs.readdirSync(fileUrl);//讀取該文件夾
            files.forEach(function (file) {
                let stats = fs.statSync(fileUrl + '/' + file);
                if (stats.isDirectory()) {
                    emptyDir(fileUrl + '/' + file);
                } else {
                    fs.unlinkSync(fileUrl + '/' + file);
                    console.log("刪除文件" + fileUrl + '/' + file + "成功");
                }
            });
        } catch (e) {
            console.log(e);
        }
    };
    emptyDir(commonPath);

    return merge(common, {
        output: {
            publicPath: publicPath
        },
        plugins: [
            new UglifyJSPlugin({
                output: {
                    quote_keys: true,
                    keep_quoted_props: true,
                    screw_ie8: false
                },
                mangleProperties: {
                    screw_ie8: false
                },
                compress: {
                    properties: false,
                    screw_ie8: false
                },
                mangle: {
                    screw_ie8: false,
                    except: ['e']
                },
                sourceMap: false
            }),
            //項目是用了autoprefix自動添加前綴,這樣壓縮,會導致添加的前綴丟失
            new OptimizeCssAssetsPlugin({ //https://zhuanlan.zhihu.com/p/37251575
                assetNameRegExp: /\.css\.*(?!.*map)/g, //注意不要寫成 /\.css$/g
                cssProcessor: require('cssnano'),
                cssProcessorOptions: {
                    discardComments: { removeAll: true },
                    // cssnano 集成了autoprefixer的功能
                    // 會使用到autoprefixer進行無關前綴的清理
                    // 關閉autoprefixer功能
                    // 使用postcss的autoprefixer功能
                    autoprefixer: false
                },
                canPrint: true
            }),
            new webpack.DefinePlugin({
                'process.env': {
                    'NODE_ENV': JSON.stringify('production')
                }
            })
        ]
    })
};

 

 

webpack.prod.js

const merge = require('webpack-merge'); 
module.exports = function(env){ const test = require('./webpack.test.js')(env);
const publicPath = test.output.publicPath.replace(/test.jojo.com/, "www.jojo.com");

//線上環境js和css的路徑 return merge(test, { output: { publicPath: publicPath } }) };

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM