摘要:指定啟用例如上述代碼,就使用和處理了除了以外的。設(shè)置當(dāng)前的為,同樣這個(gè)配置也可以寫在中。設(shè)置目錄刪除注釋去除空格去除屬性引號(hào)復(fù)制靜態(tài)目錄將所以可能被請(qǐng)求的靜態(tài)文件,分別放在目錄下。結(jié)語本次從零到一,新建了一個(gè)腳手架。
react-sample-javascript
為了實(shí)現(xiàn)一個(gè)可定制化高的react工程,我們往往會(huì)自己搭建一個(gè)react工程。所以本文會(huì)從零開始搭建一個(gè)react腳手架工程。解釋webpack中配置的含義。
基于webpack 4.0 。包含 開發(fā)環(huán)境配置,生產(chǎn)環(huán)境配置,代碼分離,css提取,gzip壓縮,base64加載資源,打包分析,ssh一鍵部署等常用配置。
github項(xiàng)目地址
[TOC]
配置 .editorconfig 使得IDE的方式統(tǒng)一 (見代碼)
配置 .eslintrc.js 使得代碼規(guī)范統(tǒng)一 (見代碼)
預(yù)期功能管理資源: 能加載css、sccc、less、以及靜態(tài)文件
管理輸出:將打包后的靜態(tài)文件輸出至static目錄下,以各自的文件類型管理
dev:使用source map,方便調(diào)試時(shí)代碼定位
dev:配置devServer,并配置熱替換,熱加載,自動(dòng)刷新,自動(dòng)打開瀏覽器,并預(yù)留proxyTable
dev:設(shè)置默認(rèn)打開8080,被占用則尋找下一個(gè)空接口
production:代碼分離,打包c(diǎn)ss文件,css代碼壓縮,js代碼壓縮,輸出到模板html,配置gzip
analysis::使用BundleAnalyzerPlugin 分析打包后的性能
目錄結(jié)構(gòu):. │ .babelrc #babel的規(guī)則以及插件 │ .editorconfig #IDE/編輯器相關(guān)的配置 │ .eslintignore #Eslint忽視的目錄 │ .eslintrc.js #Eslint的規(guī)則和插件 │ .gitignore #Git忽視的目錄 │ .postcssrc.js #postcss的插件 │ package-lock.json │ package.json #項(xiàng)目相關(guān)的包 │ README.md │ yarn.lock │ ├─build #webpack相關(guān)的配置 │ utils.js #webpack配置中的通用方法 │ webpack.base.conf.js #webpack的基礎(chǔ)配置 │ webpack.dev.conf.js #webpack的開發(fā)環(huán)境配置 │ webpack.prod.conf.js #webpack的生產(chǎn)環(huán)境配置 │ └─src #主目錄,業(yè)務(wù)代碼 │ app.css │ App.js │ favicon.ico │ index.ejs │ index.js │ └─assets #靜態(tài)目錄,存放靜態(tài)資源 │ config.json │ └─img logo.svg安裝依賴
eslint-loader
eslint
eslint-config-airbnb
eslint-plugin-import
eslint-friendly-formatter
eslint-plugin-flowtype
eslint-plugin-jsx-a11y
eslint-plugin-react
babel-polyfill
webpack
jest
friendly-errors-webpack-plugin 編譯提示的webpack插件
html-webpack-plugin 新建html入口文件的webpack插件
copy-webpack-plugin webpack配置合并模塊
webpack-merge webpack配置合并模塊
webpack-dev-server
webpack-bundle-analyzer
webpack-cli
portfinder 尋找接口的插件
extract-text-webpack-plugin
node-notifier
optimize-css-assets-webpack-plugin
autoprefixer
mini-css-extract-plugin
autoprefixer
css-loader
less-loader
postcss-loader
postcss-import
postcss-loader
style-loader
babel-core
babel-eslint
babel-loader
babel-plugin-transform-runtime
babel-plugin-import
babel-preset-env
babel-preset-react
babel-polyfill
url-loader
cross-env
file-loader
yarn add eslint eslint-loader eslint-config-airbnb eslint-plugin-import eslint-friendly-formatter eslint-plugin-flowtype eslint-plugin-jsx-a11y eslint-plugin-react babel-polyfill webpack jest webpack-merge copy-webpack-plugin html-webpack-plugin friendly-errors-webpack-plugin webpack-dev-server webpack-bundle-analyzer webpack-cli portfinder extract-text-webpack-plugin node-notifier optimize-css-assets-webpack-plugin autoprefixer mini-css-extract-plugin autoprefixer css-loader less-loader postcss-loader postcss-import postcss-loader style-loader babel-core babel-eslint babel-loader babel-plugin-transform-runtime babel-plugin-import babel-preset-env babel-preset-react babel-polyfill url-loader cross-env file-loader -D項(xiàng)目配置 webpack 基礎(chǔ)配置
為了控制開發(fā)環(huán)境和生產(chǎn)環(huán)境,我們可以新建build文件夾。分別書寫開發(fā)環(huán)境和生產(chǎn)環(huán)境的webpack配置文件,這樣也更可以方便我們分別控制生產(chǎn)環(huán)境和開發(fā)環(huán)境。
為了提高代碼的復(fù)用率,也為了區(qū)別 基礎(chǔ)配置 和 個(gè)性配置 ,可以分別新建webpack.base、webpack.dev 和 webpack.prod三個(gè)配置文件。首先配置最基礎(chǔ)的entry(入口)和output(出口)。
module.exports = { context: path.resolve(__dirname, "../"), //絕對(duì)路徑。__dirname為當(dāng)前目錄。 //基礎(chǔ)目錄用于從配置中解析入口起點(diǎn)。因?yàn)閣ebpack配置在build下,所以傳入 "../" entry: { app: ("./src/index.js") //項(xiàng)目的入口 }, output: { path: path.resolve(__dirname, "../dist"), filename: "[name].[hash:8].js", publicPath: "/", libraryTarget: "umd", }, }entry
entry可以分別為字符串、數(shù)組和對(duì)象。
倘若應(yīng)用只有一個(gè)單一的入口,entry的值可以使用任意類型,不會(huì)影響輸出結(jié)果。
// entry為字符串 { entry: "./src/index.js", output: { path: "/dist", filename: "bundle.js" } } // 結(jié)果會(huì)生成 "/dist/bundle.js"
// entry為數(shù)組,可以添加多個(gè)彼此不互相依賴的文件。結(jié)合output.library選項(xiàng),如果傳入數(shù)組,則只導(dǎo)出最后一項(xiàng)。 { //如果你在html文件里引入了"bable-polyfill",可以通過數(shù)組將它加到bundle.js的最后。 entry: ["./src/index.js", "babel-polyfill"] , output:{ path: "/dist", filename: "bundle.js" } }
// entry為對(duì)象,可以將頁面配置為多頁面的而不是SPA,有多個(gè)html文件。通過對(duì)象告訴webpack為每個(gè)入口,成一個(gè)bundle文件。 // 多頁面的配置,可能還要借助于HtmlWebpackPlugin,來指定每個(gè)html需要引入的js { entry: { index: "./src/index.js" main: "./src/index.js" login: "./src/login.js" } output:{ path: "/dist/pages" filename: "[name]-[hash:5].js" //文件名取自"entry"對(duì)象的鍵名,為了防止推送代碼后瀏覽器讀緩存,故再生成的文件之后加上hash碼。 } } // 會(huì)分別生成index.js,main.js,login.js三個(gè)文件
關(guān)于 webpack構(gòu)建多頁面 可以參考這篇文章。不過現(xiàn)在webpack4.x也是一次斷崖式升級(jí),感興趣的同學(xué)可以自行搜索。
// entry也可以傳入混合類型 { entry:{ vendor: ["jquery","amap","babel-polyfill"] //也可以借助CommonsChunkPlugin提取vendor的chunk。 index: "./src/index.js" } output: { path: "/dist" filename: "[name]-[hash:5].js" } }
CommonsChunkPlugin在webpack4.0之后移除了,可以使用splitChunksPlugin代替。output可以參閱如下鏈接:optimization.splitChunks
output最基礎(chǔ)的兩個(gè)配置為 path 和 filename :
path 告訴 webpack的輸出目錄在那里,一般我們會(huì)設(shè)置在根目錄的 dist 文件夾;
filename 用于指定輸出文件的文件名,如果配置了創(chuàng)建了多個(gè)多帶帶的 chunk 則可以使用 [name].[hash] 這種占位符來確保每個(gè)文件有唯一的名稱;
另一個(gè)常見配置 publicPath 則是用于更加復(fù)雜的場(chǎng)景。舉例:在本地時(shí),你可能會(huì)使用 ../assets/test.png 這種url來載入圖片。而在生產(chǎn)環(huán)境下,你可能會(huì)使用CDN或者圖床的地址。那么就需要配置 publicPath = "http://cdn.example.com/assets/" 來實(shí)現(xiàn)生產(chǎn)模式下編譯輸出文件時(shí)自動(dòng)更新url。
output: { path: path.resolve(__dirname, "../dist"), filename: "[name].[hash:8].js", publicPath: "/", },resolve
resolve常用的兩個(gè)配置為 alias 和 extensions :
alias 創(chuàng)建import或者require的別名
extensins 自動(dòng)解析文件拓展名,補(bǔ)全文件后綴
resolve: { // 自動(dòng)解析文件擴(kuò)展名(補(bǔ)全文件后綴)(從左->右) // import hello from "./hello" (!hello.js? -> !hello.jsx? -> !hello.json) extensions: [".js", ".jsx", ".json"], alias: { "@": resolve("src") } },module
module的選項(xiàng)決定了如何處理項(xiàng)目中的不同類型的模塊。其中常用的有 rules 和 noParese 兩個(gè)配置項(xiàng)。
noParese 是為了防止weback解析與所有與rule相匹配的文件。目的是,忽略大型的library可以提高構(gòu)建性能。
noParse: function(content) { return /jquery|lodash/.test(content); }
rules 用于在創(chuàng)建模塊是,匹配規(guī)則數(shù)組,以確定哪些規(guī)則能夠?qū)odule應(yīng)用loader,或者是修改parser。
module: { rules: [ { test: /.(js|jsx)$/, exclude: /node_modules/, enforce: "pre", use: [{ loader: "babel-loader", }, { loader: "eslint-loader", // 指定啟用eslint-loader options: { formatter: require("eslint-friendly-formatter"), emitWarning: false } }] }, { test: /.css$/, include: /node_modules/, use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { plugins: () => [autoprefixer({ browsers: "last 5 versions" })], sourceMap: false, }, }, ], }, { test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: ("assets/img/[name].[hash:7].[ext]") } }, { test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: ("assets/media/[name].[hash:7].[ext]") } }, { test: /.(woff2?|eot|ttf|otf)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: ("assets/fonts/[name].[hash:7].[ext]") } } ] }
例如上述代碼,就使用eslint-lodaer 和 babel-loader 處理了除了node_modules 以外的 js||jsx。同時(shí)配置了,解析圖片、視頻、字體文件等的解析,當(dāng)rules匹配到的文件時(shí),小于10000 byte 時(shí),采用url-loader解析文件。
Webpack開發(fā)配置因?yàn)樵趙ebpack 4.X 中使用了流行的 ”約定大于配置“ 的做法,所以在新加入配置項(xiàng) mode ,可以告知webpack使用相應(yīng)模式的內(nèi)置優(yōu)化。
選項(xiàng) | 描述 |
---|---|
development | 會(huì)將process.env.NODE_ENV 的值設(shè)為 development 。啟用 NamedChunksPlugin 和 NamedMoudulesPlugin。 |
production | 會(huì)將process.env.NODE_ENV 的值設(shè)為 production 。啟用 FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin 和UglifyJsPlugin。 |
如果我們只設(shè)置NODE_ENV,則不會(huì)自動(dòng)設(shè)置 mode
在開發(fā)時(shí),我們往往希望能看到當(dāng)前開發(fā)的頁面,并且能熱加載。這時(shí),我們可以借助webpack-dev-server 這個(gè)插件,來在項(xiàng)目中起一個(gè)應(yīng)用服務(wù)器。
// package.json "scripts": { "start": "webpack-dev-server --mode development --config build/webpack.dev.conf.js", } // 設(shè)置當(dāng)前的mode為development,同樣這個(gè)配置也可以寫在webpack.dev.conf.js中。然后使用build目錄下的webpack.dev.conf.js 來配置相關(guān)的webpack。
devServer: { clientLogLevel: "warning", historyApiFallback: true, //在開發(fā)單頁應(yīng)用時(shí)非常有用,它依賴于HTML5 history API,如果設(shè)置為true,所有的跳轉(zhuǎn)將指向index.html contentBase: path.resolve(__dirname, "../src"), compress: true, hot: true, // 熱加載 inline: true, //自動(dòng)刷新 open: true, //自動(dòng)打開瀏覽器 host: HOST||"localhost", port: PORT, overlay: { warnings: false, errors: true }, // 在瀏覽器上全屏顯示編譯的errors或warnings。 publicPath: "/", proxy: {}, quiet: true, // necessary for FriendlyErrorsPlugin // 終端輸出的只有初始啟動(dòng)信息。 webpack 的警告和錯(cuò)誤是不輸出到終端的 watchOptions: { poll: false } }, plugins: [ new webpack.DefinePlugin({ ...process.env }), //開啟HMR(熱替換功能,替換更新部分,不重載頁面!) new webpack.HotModuleReplacementPlugin(),// HMR shows correct file names in console on update. //顯示模塊相對(duì)路徑 new webpack.NamedModulesPlugin(), //不顯示錯(cuò)誤信息 new webpack.NoEmitOnErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin ]
其實(shí)在開發(fā)時(shí),我們可以設(shè)置 contentBase: "/src" ,contentBase 指定了devServer能訪問的資源地址。因?yàn)槲覀冮_發(fā)時(shí),資源大部分都放在src目錄下,所以可以直接指定資源路徑為src目錄。因?yàn)槲覀冊(cè)趙ebpack基礎(chǔ)配置時(shí),配置了 output 輸出為 dist 目錄,所以我們也可以在devServer里,設(shè)置 contentBase 為 dist 目錄。不過此時(shí)需要使用copyWebpackPlugin將一些靜態(tài)資源復(fù)制到 dist 目錄下,手動(dòng)新建dist目錄,并復(fù)制也可以。
另外,當(dāng)使用 history 路由時(shí),要配置 historyApiFallback = true ,以此讓服務(wù)器放棄路由權(quán)限,交由前端路由。而是用 hash 路由則不需要此配置。
項(xiàng)目進(jìn)階 生產(chǎn)環(huán)境配置在使用webpack 4.x 的 mode 配置之后,需要我們手動(dòng)配置的項(xiàng)已經(jīng)減少了很多,像js代碼壓縮這種工具 UglifyJsPlugin 就已經(jīng)不用手動(dòng)去配置。但是像很多前面提到的 代碼分離 、css代碼提取和壓縮 、html的生成 以及 復(fù)制靜態(tài)資源 還需要我們手動(dòng)配置。
代碼分離// 設(shè)置代碼分離的輸出目錄 output: { path: path.resolve(__dirname, "../dist"), filename: ("js/[name].[hash:8].js"), chunkFilename: ("js/[name]-[id].[hash:8].js") }, // 代碼分離 optimization: { runtimeChunk: { name: "manifest" }, splitChunks: { chunks: "all" } },
可以參閱如下鏈接:optimization.splitChunkscss代碼壓縮
借助 MiniCssExtractPlugin 來實(shí)現(xiàn)壓縮css和提取css。因?yàn)?MiniCssExtractPlugin 無法與style-loader 共存,所以我們需要判斷當(dāng)前環(huán)境是生成環(huán)境還是開發(fā)環(huán)境。
我們可以新建一個(gè)util.js的文件,在webpack當(dāng)中一些共用的方法。考慮使用個(gè)別配置字段 extract 來配置使用何種方式來配置css-loader。參見 util.js 代碼。
new MiniCssExtractPlugin({ filename: "css/[name].[hash:8].css", chunkFilename: "css/[name]-[id].[hash:8].css", }),生成HTML
使用htmlWebpackPlugin,配合ejs。可以使控制html 的生成。通過配置的方式,生成html。因?yàn)?HtmlWebpackPlugin 本身可以解析ejs,所以不需要多帶帶引入ejs的loader。
new HtmlWebpackPlugin({ filename: "index.html", template: "./src/index.ejs", // 設(shè)置目錄 title: "React Demo", inject: true, // true->"head" || false->"body" minify: { //刪除Html注釋 removeComments: true, //去除空格 collapseWhitespace: true, //去除屬性引號(hào) removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: "dependency" }),
復(fù)制靜態(tài)目錄<%= htmlWebpackPlugin.options.title %> <% for (var chunk in htmlWebpackPlugin.files.css) { %> <% } %> <% for (var chunk in htmlWebpackPlugin.files.chunks) { %> <% } %>
將所以可能被請(qǐng)求的靜態(tài)文件,分別放在assets目錄下。那么在打包后,為了保證目錄能正常訪問(不使用CDN等加載靜態(tài)資源時(shí)),我們可以配置 publicPath = "/" 。然后借助于 CopyWebpackPlugin 實(shí)現(xiàn)資源復(fù)制。
new CopyWebpackPlugin([{ from: "./src/assets/", to: "assets" }]),
將 src/assets 復(fù)制到 dist/assets 目錄下。
開啟打包分析借助插件 BundleAnalyzerPlugin 直接在plugins中創(chuàng)建該插件:
// webpack.prod.conf.js const BundleAnalyzerPlugin = process.env.NODE_ENV=== "analysis" ? require("webpack-bundle-analyzer").BundleAnalyzerPlugin:null process.env.NODE_ENV=== "analysis" ? new BundleAnalyzerPlugin() : ()=>{}
在package.json 中可做如下配置:
"scripts": { "analysis": "cross-env NODE_ENV=analysis webpack -p --mode production --progress --config ./build/webpack.prod.conf.js ", },
通過注入環(huán)境變量,來控制是否運(yùn)行打包分析。
ssh部署打包后的dist文件夾,可以直接借助 node 的 ssh-node ,直接部署到服務(wù)器指定的目錄下。 ssh-node既支持ssh,也支持密碼登錄。建議可以為在每個(gè)項(xiàng)目下,新建一個(gè).ssh文件,存放項(xiàng)目的私鑰。代碼如下:
// usage: https://www.npmjs.com/package/node-ssh var path, node_ssh, ssh, fs, opn, host fs = require("fs") path = require("path") node_ssh = require("node-ssh") opn = new require("opn") ssh = new node_ssh() host = "localhost" var localDir = "./dist" var remoteDir = "/opt/frontend/new" var removeCommand = "rm -rf ./*" var pwdCommand = "pwd" ssh.connect({ host: host, username: "root", port: 22, // password, privateKey: "./.ssh/id_rsa", }) .then(function() { ssh.execCommand(removeCommand, { cwd:remoteDir }).then(function(result) { console.log("STDOUT: " + result.stdout) console.log("STDERR: " + result.stderr) ssh.putDirectory(localDir, remoteDir).then(function() { console.log("The File thing is done") ssh.dispose() opn("http://"+host, {app:["chrome"]}) }, function(error) { console.log("Something"s wrong") console.log(error) ssh.dispose() }) }) })
此時(shí),在命令行直接 node deploy.js 就可以運(yùn)行以上腳本,我們也可以添加一個(gè)build + deploy的script腳本,便于啟動(dòng)。
"scripts": { "depoly": "npm run build && node ./deploy.js", }結(jié)語
本次從零到一,新建了一個(gè)react腳手架。過程中有很多問題,也參考了不少大牛的解釋。代碼里也有諸多問題。還望各位看官,不吝指教。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/108085.html
摘要:只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙只有動(dòng)手,你才能真正掌握一門技術(shù)持續(xù)更新中項(xiàng)目地址求求求源碼系列跟一起學(xué)如何寫函數(shù)庫中高級(jí)前端面試手寫代碼無敵秘籍如何用不到行代碼寫一款屬于自己的類庫原理講解實(shí)現(xiàn)一個(gè)對(duì)象遵循規(guī)范實(shí)戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙 只有動(dòng)手,你才能真正掌握一門技術(shù) 持續(xù)更新中…… 項(xiàng)目地址 https...
摘要:廢話不多話,來上車安裝或者簡述使用創(chuàng)建全局的樣式首先創(chuàng)建一個(gè)文件,例如引全局包里面為項(xiàng)目需要的內(nèi)容在組件內(nèi)把引入的當(dāng)做標(biāo)簽寫入創(chuàng)建一個(gè)局部的樣式引局部包里面為項(xiàng)目需要的內(nèi)容在組件內(nèi)把引入的當(dāng)做標(biāo)簽寫入類嵌套類似于用法大同小異列舉 廢話不多話,來上車! 安裝: npm install --save styled-components (或者 yarn add styled-com...
摘要:原文從零到一,擼一個(gè)在線斗地主上篇作者背景朋友來深圳玩,若說到在深圳有什么好玩的,那當(dāng)然是宅在家里斗地主了可是天算不如人算,撲克牌丟了幾張不全大熱天的,誰愿意出去買牌啊。 原文:從零到一,擼一個(gè)在線斗地主(上篇) | AlloyTeam作者:TAT.vorshen 背景:朋友來深圳玩,若說到在深圳有什么好玩的,那當(dāng)然是宅在家里斗地主了!可是天算不如人算,撲克牌丟了幾張不全……大熱天的,...
閱讀 857·2021-10-11 10:59
閱讀 2792·2019-08-30 15:43
閱讀 2129·2019-08-30 11:08
閱讀 1647·2019-08-29 15:20
閱讀 1002·2019-08-29 13:53
閱讀 486·2019-08-26 13:24
閱讀 1633·2019-08-26 13:24
閱讀 2820·2019-08-26 12:08