前言
前幾天做了一個項目:【node】記錄項目的開始與完成——pipeline_kafka流式數據庫管理項目;因為開發時間緊迫,淺略的使用了一下react,感覺這個ui庫非常的符合我的口味,現在趁着有空閑時間,將項目前端重構一番。這里面存在一些坑,都是深不見底的水坑,說多了都是淚水。。。好在順利完成,現在在這里再一步一步重來一遍,和需要學習webpack的前端童鞋分享。
准備
tips:文章最后可下載demo
一:目錄
首先我們要新建目錄,
新建app文件夾,它存放入口文件,component組件,
新建static文件夾存放打包后的文件,
新建webpack文件夾,存放的webpack配置文件。
二、生成package.json
在當前目錄打開cmd或者PowerShell或者終端,
輸入npm init 然后一直回車到執行完畢,package.json就滾到文件夾根目錄下面了
三、安裝webpack
shell輸入 npm i webpack -g 安裝webpack
我成功后的提示是這樣的。
四、新建文件
在目錄app中新建main.js,
在目錄webpack中新建webpack的配置文件 webpack.config.js
在目錄static中新建一個 index.html
在目錄static中新建一個js目錄
五、初步配置
首先寫一下 /static/index.html這個文件,因為生成的文件目錄在 /static/js文件夾里面,所以這里要預先引用打包后的文件,最后訪問這個html文件就可以看到效果
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title>webpack_Demo</title> </head> <body> <div class="content"> </div> <script src="./js/app.js" type="text/javascript" charset="utf-8"></script> </body> </html>
然后配置webpack.config.js:
剛開始我們就配一個簡單的入口和輸出目錄就可以了:
var path = require("path"); module.exports = { entry:{ //入口文件 "app":path.join(__dirname,"../app/main.js") //app對應生成的文件名 }, output:{ path:path.join(__dirname,"../static/js/"), filename:"[name].js" //這里[name]就是表示對應entry對象的name,然后生成的后戳是.js } }
現在我們就可以簡單的測試一下,在/app/main.js中隨意寫一些代碼 ,比如alert(1)。
在shell中調用webpack測試,運行:
webpack --config ./webpack/webpack.config.js
成功后訪問index.html,查看效果,如果報錯,可能是缺少哪個依賴包,安裝后重復上面步驟。
這個時候文件目錄就變成了:
app.js就是打包后生成的文件。
現在我們要把打包命令放在package.json里,因為每次編譯都要運行那么長的命令,太痛苦了。在package.json簡單的配置一個script就可以使用npm run xxx運行了,編輯package.json:
{ "name": "web_pack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build":"webpack --config ./webpack/webpack.config.js" //添加一個build 值是打包用到的命令 }, "author": "", "license": "ISC", "dependencies": { "webpack": "^3.0.0" } }
現在在shell里運行 npm run build就可以編譯了
到這一步,初步的配置就算是完成了,接下來配置一個webpack-dev-server,然后就可以配置react組件,生成項目了
配置webpack-dev-server
什么是webpack-dev-server?
它是用來監控文件的修改事件,啟動它的時候,會分配一個端口,指向當前的目錄,一旦目錄下文件被修改,它會通知瀏覽器自動刷新頁面,省去了不斷的按f5的煩惱。
在shell中安裝,運行:
npm i webpack-dev-server --save-dev
安裝成功后,運行:
node_modules/.bin/webpack-dev-server --config ./webpack/webpack.config.js --port 8089 --open
--config 是webpack配置文件目錄,--port 是運行的端口號
運行成功會在系統默認瀏覽器彈出一個窗口,這是一個選擇文件夾的界面,我們訪問static,就可以訪問到index.html了:
這里需要注意的一點是,你還需要將index.html中的app.js引用改成http://localhost:8089/app.js,因為webpack-dev-server加載的是虛擬文件,目錄在根目錄下,所以需要修改這里。
<script src="http://localhost:8089/app.js" type="text/javascript" charset="utf-8"></script>
項目完成后再改成對應的路徑。
修改完成,修改一下main.js中的js代碼,再看看瀏覽器中頁面是否改變。webpack-dev-server大致就配置好了,另外附上它的api地址:
最后再把命令配置到package.json中
{ "name": "web_pack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "node_modules\.bin\webpack-dev-server --config ./webpack/webpack.config.js --port 8089 --open", //添加到test中 "build":"webpack --config ./webpack/webpack.config.js" }, "author": "", "license": "ISC", "dependencies": { "webpack": "^3.0.0" } }
現在運行npm run test 就可以開始開發了。
配置react
到了這一步,我們要開始配置react,首先還是安裝依賴包,編譯jsx文件需要用到babel、babel-core、babel-loader、babel-preset-es2015、babel-preset-react、還有react自己的react、react-dom。安裝他們:
npm i babel babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom --save
安裝成功之后,先在main.js寫一個demo
var React = require("react"); var ReactDOM = require("react-dom"); ReactDOM.render( <div> <h1>welcome</h1> </div> ,document.querySelector(".content") );
這里一定要用 ReactDOM.render 老的版本react.render會報語法錯誤。
配置babel-loader
在webpack.config.js添加babel-loader用來解析jsx和es6的代碼:
var path = require("path"); module.exports = { entry:{ "app":path.join(__dirname,"../app/main.js") //入口文件 name對應輸出的[name] }, output:{ path:path.join(__dirname,"../static/js/"), //輸出路徑 filename:"[name].js" //輸出app.js }, module:{ loader:[ { test:/\.(js|jsx)$/, //這是配置加載文件的規則 值是正則表達式 這里寫的意思是.js .jsx結尾的文件加載loader loader:"babel-loader", exclude:/node_module/, //這個目錄不需要加載loader query:{ presets:["react","es2015"] //加載loader的presets } } ] } }
配置了module下面的loaders。
然后npm run test 開啟服務,測試一下代碼是否可以運行。
順利的話,這里應該能看到welcome了:
現在的代碼結構顯然過於簡單,下面我們在app文件目錄下新建兩個目錄 component 和 views。
先在component 文件夾下新建hello.jsx,寫一些代碼:
import React from "react"; class Hello extends React.Component{ constructor(props){ super(props); this.propTypes = { text:React.PropTypes.string } } render(){ return <div className="hello"> hello <span>{this.props.text}</span> </div> } } module.exports = Hello;
一般我的import 語法用來表示react組件或其他資源的引入,require語法用來表示js的引入,另外這里用的是es6的class語法。關於es6,在文章下面有淺略說明。
然后再views新建index.jsx,這里可以寫主要的頁面代碼:
import React from 'react'; import Hello from "../component/hello.jsx"; //引入hello組件 class Index extends React.Component{ render(){ return <div className="index_container"> <Hello text="world"></Hello> </div> } } module.exports = Index;
最后main.js引入index,把index加入rander就可以看到效果了:
var React = require("react"); var ReactDOM = require("react-dom"); import Index from "./views/index.jsx"; //引入index ReactDOM.render( <div> <Index></Index> //插入index視圖 </div> ,document.querySelector(".content") );
查看效果:
到了這一步,基本的視圖結構就完成了,接下來配置css的加載,基本和react的方式一樣,加載對應的loader,解析對應的文件。
配置css:
視圖解決了,現在我們要解決css的引入問題,這里可以選擇的就比較多 css、sass、less等都可以,我選擇的是css,因為我的sass文件可以使用ruby的sass編譯,在編輯保存時已經自動編譯成css了,個人覺得這樣管理起來更加方便。
首先還是安裝依賴,運行:
npm i css-loader file-loader style-loader url-loader --save-dev
file-loader url-loader 是用來編譯圖片資源的,它會將url()中的靜態資源打包編譯成base64格式,這里需要注意,不要在css中寫找不到文件的路徑,否則會報錯編譯失敗。
安裝完成后配置css-loader:
var path = require("path"); module.exports = { entry:{ "app":path.join(__dirname,"../app/main.js") //入口文件 name對應輸出的[name] }, output:{ path:path.join(__dirname,"../static/js/"), //輸出路徑 filename:"[name].js" //輸出app.js }, module:{ loaders:[ { test:/\.(js|jsx)$/, //這是配置加載文件的規則 值是正則表達式 這里寫的意思是.js .jsx結尾的文件加載loader loader:"babel-loader", exclude:/node_module/, //這個目錄不需要加載loader query:{ presets:["react","es2015"] //加載loader的presets } }, { test:/\.css$/, //配置.css后戳的解析 loader:"style-loader!css-loader" },
{
test:/\.(png|jpg)$/, //配置靜態文件解析
loader:"url-loader?limit=8192"
} ] } }
注意style-loader必須寫在css-loader前面,否則就會報錯
配置完成,在hello.jsx引入個css瞧瞧(這里要重啟一下test):
引入的方法是
import "./hello.css";
引入成功,這樣css就可以使用了
項目結構
到目前為止 項目的結構是這樣的:
app中存放 組件(component)、視圖(views)、入口文件 。
static中存放打包后的項目文件。
webpack中存放webpack的配置文件
建議將公共組件打包,比如建一個hello文件夾,里面存放hello.jsx和hello.css以及需要的插件、文檔,這樣它的多項目復用將變得非常方便,拷貝文件即可。
淺談es6以及react中的小坑
es6的class關鍵字看起來很性感,實際上也只是整了個容,感覺內在變化不多。與createClass差別不是很大,在react中每次生命組件都要繼承React.Component。
比如上面的hello.jsx:
constructor(props){ super(props); this.propTypes = { text:React.PropTypes.string } }
這是構造函數,就是new xxx()調用的那個函數,這里為啥要super? 這是個奇怪的寫法,原因是因為構造函數中訪問不到this,需要調用super()才能順利訪問到this,這里個人尤為不解,雖然理解起來也不難,構造函數屬於static方法,這里為啥不能和java一樣的邏輯,構造函數只能訪問類?
this.propTypes
這個屬性標明了組件中所有用到的props 並且能驗證傳入的值的正確性,感覺組件有它非常有必要,建議寫props不要少了這個屬性。
react 中this的坑
在hello.jsx組件上添加一個click事件,在事件中打印this:
class Hello extends React.Component{ constructor(props){ super(props); this.propTypes = { text:React.PropTypes.string } } render(){ return <div className="hello" onClick={this.print}> hello <span>{this.props.text}</span> </div> } print(){ console.log("點擊事件"); console.log(this); } }
會發現this的值既不是點擊dom也不是class而是null:
可是如果在靜態文件中寫React.createClass則不會出現null。
我的解決辦法是在render中強制指定this為class:
render(){ this.print = this.print.bind(this);//綁定this到函數 return <div className="hello" onClick={this.print}> hello <span>{this.props.text}</span> </div> }
查看官方文檔解釋是:
總結
前端就像是一只正當壯年的母雞,今天下個蛋,明天下個蛋,是寡蛋是好蛋不知道,今天的蛋和明天的蛋嘗起來味道也差不多。我們就像是飼養員,負責給母雞送吃的,下了蛋就要馬上負責收,否則要么就爛了、要么就孵出小雞吃不了雞蛋了。
那些層出不窮的框架,壞掉的框架、已經變的強大就很難駕馭的框架,這對前端來說是一個考驗。