>>創建nodejs工程css
新建文件夾,npm init 生成package.jsonhtml
>>安裝webpack 和 webpack-dev-server前端
npm install --save-dev webpack@3.8.1 注意4.x版本語法有些變化node
npm install --save-dev webpack-dev-server@2.9.7 注意踩坑記錄1react
>>安裝babel轉碼es6jquery
Babel實際上是幾個模塊化的包,其核心功能位於稱爲babel-core
的npm包中,webpack能夠把其不一樣的包整合在一塊兒使用,對於每個你須要的功能或拓展,你都須要安裝單獨的包(用得最多的是解析Es6的babel-env-preset
包和解析JSX的babel-preset-react
包)。webpack
babel 6 與 bable-loader 7匹配,ios
另外解決promise等,用 babel-polyfill,bebel6對應版本爲babel-polyfill 6,解決組件按需加載編譯,用 babel-plugin-importgit
>>支持 react 開發es6
npm install --save-dev react react-dom 注意這裏是本地安裝,也能夠用全局安裝
安裝其餘可選插件:
>>配置webpack.config.js
踩坑記錄
1:webpack是3.x版本的,webpack-dev-server是3.x的版本,這兩個版本不兼容,能夠把webpack-dev-server降到2.x版本
踩坑解決辦法示例:TypeError: Cannot read property 'compile' of undefined #1334
解決思路: 優先使用 Google
引擎進行搜索關鍵詞句, 好比 webpack Cannot read property 'compile' of undefined
;看可否找到相應的問題。
若是不行,不妨換一種方式再搜索,譬如:site:stackoverflow.com webpack Cannot read property 'compile' of undefined
,在具體某個網站下搜索;若是仍是沒能找看法決辦法的話,能夠在各類平臺提問,好比 segmentfault。
額外補充: 對於 Google
這個工具還真是有必要先學,具體經常使用操做可參見:如何更好地運用 Chrome (Google)。假若,不可以沒有適用 Google
的環境,那麼這裏整理集結若干優質搜索引擎,堪稱 Google 搜索優質替代品,可供參考。
package.json 示例:
{ "name": "webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.config.js --watch" }, "author": "", "license": "ISC", "devDependencies": { "antd": "^3.25.0", "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-import": "^1.12.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-runtime": "^6.23.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "clean-webpack-plugin": "^1.0.0", "copy-webpack-plugin": "^4.6.0", "css-loader": "^3.2.0", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^4.2.0", "html-webpack-plugin": "^3.2.0", "less-loader": "^5.0.0", "sass-loader": "^7.3.1", "style-loader": "^1.0.0", "uglifyjs-webpack-plugin": "^1.3.0", "url-loader": "^2.1.0", "webpack": "^3.8.1", "webpack-bundle-analyzer": "^3.1.0", "webpack-dev-server": "^2.9.7" }, "dependencies": { "react": "^16.9.0", "react-dom": "^16.9.0" } }
webpack.config.js 示例:
const path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const CleanWebpackPlugin = require("clean-webpack-plugin"); // const ExtractTextPlugin = require("extract-text-webpack-plugin"); // const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { // context: path.resolve(__dirname), //webpack運行時的根目錄,默認爲當前目錄 // 配置尋找模塊的規則 resolve: { modules: [ // 尋找模塊的根目錄,array 類型,默認以 node_modules 爲根目錄 path.resolve(__dirname), 'node_modules' ], extensions: ['.js', '.jsx', '.json', '.css', '.less', '.scss'], // 當模塊沒有後綴名時,webpack嘗試添加後查找文件,經常使用的放前面 alias: { // 別名,用於替換import導入模塊的路徑,例如可在dev環境導入不壓縮的包,prd環境導入壓縮的包 "react": "react/cjs/react.production.min.js", // 將import react替換爲import "react.min.js" "react-dom": "react-dom/cjs/react-dom.production.min.js", // "LP": "xxx/lp.min.js", // 將"LP"替換爲xxx/lp.min.js // 'only-module$': 'new-module' // 使用結尾符號 $ 後,只把 'only-module'結尾的路徑 映射成 'new-module', // 'module/path/file' 不會被映射成 'new-module/path/file' }, enforceExtension: false, // 是否強制導入語句必需要寫明文件後綴 }, // entry 表示入口文件,注意這裏的路徑拼上resolve.modules路徑即嘗試加載入口文件的完整路徑 // 類型能夠是 string | object | array // entry: "src/js/entry.js", // 只有1個入口,入口只有1個文件,對應1個chunk出口文件,且命名爲main // entry: ["src/js/entry1.js", "src/js/entry2.js"], // 只有1個入口,入口有2個文件,對應1個chunk出口文件,且命名爲main entry: { // 有2個入口,對應2個chunk出口文件。entry爲object時對應多個chunk出口文件,名稱默認爲key值。 // 'vendor': ['babel-polyfill', 'isomorphic-fetch', 'prop-types', 'react', 'react-dom', 'react-redux', 'react-router-dom', 'redux', 'react-bootstrap'], 'vendor': ['babel-polyfill', 'react', 'react-dom'], "index": "src/app/index.js" }, // entry: ()=>{ // 可由函數動態生成entry // return { // a:"xxx", // b:"yyy" // } // } // resolveLoader: { // 告訴 webpack 如何尋找Loader源文件,可用於加載本地的Loader // modules: ['node_modules'], // 去哪一個目錄尋找 // extensions: ['.js', 'json'], // 入口文件後綴 // mainFields: ['loader', 'main'] // 優先使用哪一種類型的入口,可不配置 // }, externals: { // 告訴webpack要構建的代碼中使用了哪些不用打包的模塊(一般直接在模板html的script標籤中引入),即直接使用JS運行環境提供的全局變量 jquery: 'window.jQuery' // 將導入語句裏的jqurey替換成全局變量 window.jQuery。例如代碼中有 import $ from 'jquery' 會被正確的替換 }, // 通常代碼中也不會用模塊引入的語法引入自己不打包的模塊,所以這一項也能夠不配置 // 如何輸出結果:在 Webpack 通過一系列處理後,如何輸出最終想要的代碼。 output: { // 輸出文件存放的目錄,必須是 string 類型的絕對路徑。 path: path.resolve(__dirname, 'dist'), // 引用資源的路徑 URL 前綴,拼上entry的路徑即爲資源訪問路徑。 // publicPath: 'https://cdn.example.com/', // 放到 CDN 上去,html中引入js時使用https://cdn.example.com/xxx.js // 一般本地調試和發佈到線上時訪問路徑不一樣,能夠根據參數化選擇: // publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath // 輸出文件的名稱 // filename: 'bundle.js', // 完整的名稱 // filename: '[name].js', // 當配置了多個 entry 時,經過名稱模版爲不一樣的 entry 生成不一樣的文件名稱 filename: '[name].[chunkHash:8].js', // 根據文件內容 hash 值取8位生成文件名稱,用於瀏覽器長時間緩存文件 // 導出庫的名稱,string 類型 // 不填它時,默認輸出格式是匿名的當即執行函數 // library: 'MyLibrary', // 導出庫的類型,枚舉類型,默認是 var // 能夠是 umd | umd2 | commonjs2 | commonjs | amd | this | var | assign | window | global | jsonp , // libraryTarget: 'umd', // 附加 Chunk 的文件名稱,用於指定無入口、動態加載、webpack運行過程當中生成的chunk的名稱,一般也能夠在具體的plugin中去配置 // chunkFilename: '[id].js', // chunkFilename: '[chunkhash].js' }, // 配置模塊相關 module: { rules: [ // 配置 Loader { test: /\.jsx?$/, // 正則匹配命中要使用該 Loader 的文件,能夠爲字符串或正則,或者數組類型 // include: [ // 匹配範圍包含,即只會命中這裏面的文件 // path.resolve(__dirname, 'src') // ], exclude: /node_modules/, // 匹配範圍不包含,即排除這裏面的文件 use: [{ // 使用哪些 Loader,有多個時從後往前執行,能夠經過options傳一些參數 loader: "babel-loader" }], }, { test: /\.css$/, use: ['style-loader', 'css-loader'] // 不傳參數時能夠簡寫 // 注:style-loader做用是將css編譯到js中,而後運行時插入DOM節點的style屬性,可能致使js代碼較大,且css複用性不夠。可用ExtractTextWebpackPlugin優化。 // use: ExtractTextPlugin.extract({ // fallback: "style-loader", // 編譯後用什麼loader來提取css文件並引入到html // use: "css-loader" // }) }, { test: /\.less$/, use:['style-loader','css-loader', 'less-loader'] }, { test: /\.scss$/, use:['style-loader','css-loader', 'sass-loader'] }, { // 對小圖片或字體文件直接編碼爲base64提升加載速度,當圖片超過限制時直接使用file-loader拷貝 test: /\.(png|jpe?g|gif|svg|eot|woff2?|ttf|pdf)$/, loader: 'url-loader', include: [path.resolve(__dirname, 'src')], options: { limit: 10000, // 小於10kb的才編碼爲base64,建議10kb量級的小文件才編碼 name: 'media/[name].[hash:7].[ext]' // 另外還能夠配置publicPath等參數,參見文末說明 } }, ], noParse: [ // 不用解析和處理的模塊,如jQuery等非模塊化的庫,被忽略的模塊不能包含import require define等模塊化語句 /jquery|chartjs/ // 用正則匹配 ], // noParse: (path) => { // return /jquery|chartjs/.test(path); // } }, plugins: [ new CleanWebpackPlugin(['dist']), new CopyWebpackPlugin([{ from: __dirname+'/src/resource', // 靜態資源目錄地址 to: __dirname+'/dist/resource', ignore: ["test.html"] }]), // new ExtractTextPlugin({ // 提取js的css到文件,參見文末說明 // filename: 'css/style.[contenthash:16].css', // }), new webpack.optimize.CommonsChunkPlugin({ // 從vendor chunk中提取第三方公共模塊,而後從中提取webpack運行文件到runtime name: ['vendor','runtime'], filename: '[name].[hash:7].js', minChunks: 2 // 公共模塊被引用幾回時被提取出來 }), // new webpack.optimize.CommonsChunkPlugin({ // 提取自定義公共模塊 // name: 'common', // filename: '[name].[hash:7].js', // chunks: ['first','second']//從first和second chunk中抽取commons chunk // }), // new UglifyJsPlugin(), new HtmlWebpackPlugin({ title: "首頁", // 指定頁面標題 favicon: "", // 指定頁面圖標 template: __dirname + "/src/template/index.html", // html模板文件路徑 inject: true, // true|head|body|false,當傳入 true或者 body時全部js模塊將被放置在body元素的底部,head時則會放在head元素內 // minify:{ // 壓縮HTML文件 // removeComments:true, // 移除HTML中的註釋 // collapseWhitespace:true // 刪除空白符與換行符 // }, filename: "index.html", // 輸出html文件路徑 chunks: [ // 向html script標籤中添加哪些chunk "runtime", // webpack運行文件要排在最前面優先加載 "vendor", "index" ], // excludeChunks: [ // 被排除的模塊 // "main" // ] chunksSortMode: 'manual', // 設置chunk插入到html的script標籤的順序, none|auto|dependency|manual|{function} 默認爲'auto,經常使用manual根據chunks的位置手動排序 }), ], devServer: { contentBase: path.join(__dirname, "dist"), //本地服務器所加載的頁面所在的目錄 publicPath: "/dist/", // 和output.publicPath做用同樣,本地調試用,會覆蓋output.publicPath的值,確保以/開頭以/結尾 inline: true, // 實時刷新 // hot: true, // 是否開啓模塊熱替換功能(不從新加載整個網頁的狀況下刷新模塊),默認關閉 port: 9000, // 端口改成9000 compress: true, // 是否開啓 gzip 壓縮 https: false, // 是否開啓 HTTPS 模式 // open:true // 自動打開瀏覽器 // headers: { // response中注入一些響應頭 // 'X-foo': '112233' // }, // historyApiFallback: true, // 404時定向跳轉,一應用在HTML5 History API 的單頁應用,好比訪問不到路由時跳轉到index.html // 還能夠配置proxy 實現跨域請求,參見文末 }, // http://localhost:9000/webpack-dev-server 能夠查看運行時dev-server在內存中生成的目錄結構 // 其餘配置 devtool: 'source-map', // 配置 source-map 類型,方便調試時查看源碼 profile: false, // 是否捕捉 Webpack 構建的性能信息,用於分析什麼緣由致使構建性能不佳 cache: true, // 是否啓用緩存提高構建速度 watch: true, // 是否開啓監聽模式,在文件變化時自動從新編譯(默認否,當使用devServer時默認開啓) watchOptions: { // 監聽模式選項 // 不監聽的文件或文件夾,支持正則匹配。默認爲空 ignored: /node_modules/, // 監聽到變化發生後會等待多長時間再去執行編譯,防止文件更新太快致使從新編譯頻率過高,默認爲300ms aggregateTimeout: 300, // 檢測文件是否發生變化的間隔時長,默認每隔1000毫秒詢問一次 poll: 1000 }, // 輸出文件性能檢查配置 performance: { hints: 'warning', // 有性能問題時輸出警告 hints: 'error', // 有性能問題時輸出錯誤 hints: false, // 關閉性能檢查 maxAssetSize: 200000, // 最大文件大小 (單位 bytes) maxEntrypointSize: 400000, // 最大入口文件大小 (單位 bytes) assetFilter: function(assetFilename) { // 過濾要檢查的文件 return assetFilename.endsWith('.css') || assetFilename.endsWith('.js'); } }, stats: { // 控制檯輸出日誌控制 assets: true, colors: true, errors: true, errorDetails: true, hash: true, } }; // node中的路徑 // __dirname: 老是返回被執行的 js 所在文件夾的絕對路徑 // __filename: 老是返回被執行的 js 的絕對路徑 // process.cwd(): 老是返回運行 node 命令時所在的文件夾的絕對路徑 /* devServer proxy 實現跨域 有時候咱們使用webpack在本地啓動服務器的時候,因爲咱們使用的訪問的域名是 http://localhost:8081 這樣的,可是咱們服務端的接口是其餘的, 那麼就存在域名或端口號跨域的狀況下,可是很幸運的是 devServer有一個叫proxy配置項,能夠經過該配置來解決跨域的問題,那是由於 dev-server 使用了 http-proxy-middleware 包(瞭解該包的更多用法 )。 假如如今咱們本地訪問的域名是 http://localhost:8081, 可是我如今調用的是百度頁面中的一個接口,該接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。如今咱們只須要在devServer中的proxy的配置就能夠了: 以下配置: proxy: { '/api': { target: 'http://news.baidu.com', // 目標接口的域名 // secure: true, // https 的時候 使用該參數 changeOrigin: true, // 是否跨域 pathRewrite: { '^/api' : '' // 重寫路徑 } } } 而後咱們在main.js裏面編寫以下代碼: import axios from 'axios'; axios.get('/api/widget?ajax=json&id=ad').then(res => { console.log(res); }); 在這裏請求我使用 axios 插件,其實和jquery是一個意思的。爲了方便就用了這個。 下面咱們來理解下上面配置的含義: 1. 首先是百度的接口地址是這樣的:http://news.baidu.com/widget?ajax=json&id=ad; 2. proxy 的配置項 '/api' 和 target: 'http://news.baidu.com' 的含義是,匹配請求中 /api 含有這樣的域名重定向 到 'http://news.baidu.com'來。 所以我在接口地址上 添加了前綴 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 所以會自動補充前綴,也就是說,url: '/api/widget?ajax=json&id=ad' 等價 於 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'. 3. changeOrigin: true/false 還參數值是一個布爾值,含義是 是否須要跨域。 4. secure: true, 若是是https請求就須要改參數配置,須要ssl證書吧。 5. pathRewrite: {'^/api' : ''}的含義是重寫url地址,把url的地址裏面含有 '/api' 這樣的 替換成 '', 所以接口地址就變成了 http://news.baidu.com/widget?ajax=json&id=ad; 所以就能夠請求獲得了,最後就返回 接口數據了。 */ /* https://segmentfault.com/a/1190000018987483?utm_source=tag-newest url-loader 會將引入的圖片(還有字體、音視頻等)編碼,生成dataURl並將其打包到文件中,引入這個dataURL就能訪問。若是圖片較大,編碼會消耗性能。 所以url-loader提供了一個limit參數,小於limit字節的文件會被轉爲DataURl,大於limit的內部調用file-loader進行copy。 接下來摘取幾個重要的屬性作說明 outputPath 該屬性指明咱們最終導出的文件路徑 最終導出的文件路徑 === output.path + url-loader.outputPath + url-loader.name publicPath(經常使用於生成環境) 該屬性指明咱們最終引用的文件路徑(打包生成的index.html文件裏面引用資源的前綴) 最終引用的文件路徑前綴 === output.publicPath + url-loader.publicPath + url-loader.name name 該屬性指明文件的最終名稱。 一樣的,咱們能夠直接把outputPath的內容寫到name中,同樣能夠生成對應的路徑 通過上面的說明,咱們得出結論,最終的靜態文件路徑(圖片,音頻,視頻,字體等)=== output.publicPath + url-loader.publicPath + output.path + url-loader.outputPath + url-loader.name 咱們在本地開發的時候都是localhost:8080/下面的根目錄,因此當圖片生成以下的絕對地址是不會出問題的,但是一樣的webpack配置放到生成環境上就不必定了。 由於生成環境大部分的前端靜態文件都不是在根目錄啊,有可能就是這樣的目錄結構 www/ +folder/ +static/ +css/ +img/ +js/ +index.html 這樣你開發環境的絕對路徑放到服務器上面天然就404了,因此要否則用相對路徑,要否則就統一寫死絕對路徑(針對不一樣環境添加不一樣的publicPath) (一樣道理,解釋爲何css裏面的背景圖路徑404,可是這個解決起來須要用到extract-text-webpack-plugin或者mini-css-extract-plugin這個插件,前者用於webpack4如下版本,後者是4以上版本,配置options裏面的publicPath) css 提取插件,`extract-text-webpack-plugin` 插件 或者 `mini-css-extract-plugin` 的 loader也都提供了publicPath,用來複寫提取出來的css文件中的資源的引入路徑 */
模板html示例:
<!DOCTYPE html> <head> <title><%=htmlWebpackPlugin.options.title %></title> <meta charset="utf-8" /> <style> /* 垂直居中顯示 */ .center-in-center{ position: absolute; margin: 10px; padding: 10px; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); -moz-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); -o-transform: translate(-50%, -50%); transform: translate(-50%, -50%); } </style> </head> <body> <div id="root" > </div> </body> </html>
解釋下版本號:
1.15.2對應就是MAJOR,MINOR.PATCH:1是marjor version;15是minor version;2是patch version。
MAJOR:這個版本號變化了表示有了一個不能夠和上個版本兼容的大更改。
MINOR:這個版本號變化了表示有了增長了新的功能,而且能夠向後兼容。
PATCH:這個版本號變化了表示修復了bug,而且能夠向後兼容。
當咱們使用最新的Node運行‘npm instal --save xxx',的時候,他會優先考慮使用插入符號(^)而不是波浪符號(~)了。
波浪符號(~):他會更新到當前minor version(也就是中間的那位數字)中最新的版本。放到咱們的例子中就是:body-parser:~1.15.2,這個庫會去匹配更新到1.15.x的最新版本,若是出了一個新的版本爲1.16.0,則不會自動升級。波浪符號是曾經npm安裝時候的默認符號,如今已經變爲了插入符號。
插入符號(^):這個符號就顯得很是的靈活了,他將會把當前庫的版本更新到當前major version(也就是第一位數字)中最新的版本。放到咱們的例子中就是:bluebird:^3.3.4,這個庫會去匹配3.x.x中最新的版本,可是他不會自動更新到4.0.0。
由於major version變化表示可能會影響以前版本的兼容性,因此不管是波浪符號仍是插入符號都不會自動去修改major version,由於這可能致使程序crush,可能須要手動修改代碼。