一、使用動態樣式表 1.1 LESS使用 1.2 LESS和webpack結合 希望在React項目中使用less,此時就需要webpack打包的時候順便進行less翻譯 安裝依賴: 修改webpack.config.js配置: 樣式會被webpack編譯後出現在head標簽中 1.3提煉樣式表 e ...
一、使用動態樣式表
1.1 LESS使用
全局安裝Less
npm install -g less
創建1.less文件,然後可以用lessc命令來編譯這個文件:
lessc 1.less 1.css
1.2 LESS和webpack結合
希望在React項目中使用less,此時就需要webpack打包的時候順便進行less翻譯
安裝依賴:
npm install --save-dev less-loader
npm install --save-dev css-loader
npm install --save-dev style-loader
npm install --save-dev less
修改webpack.config.js配置:
const path = require('path'); module.exports = { entry: "./www/app/main", //程式的主入口文件 //程式出口文件配置 output : { //打包文件的輸出地址 path: path.resolve(__dirname, "www/dist"), //打包的文件名稱 filename : "bundle.js", }, watch:true, //讓webpack監聽變化,自動打包 mode : "development", //關於模塊規則的配置 module: { rules: [ { // 模塊規則(配置loader,解析器等選項) test: /\.js?$/, //解析的時候匹配到的都是js文件 include: [path.resolve(__dirname, "./www/app")],//翻譯什麼文件夾 exclude: [path.resolve(__dirname, "node_modules")], //不翻譯什麼文件夾 loader : "babel-loader", options: { presets: ["env","react"], //要翻譯的ES語法 plugins: ["transform-object-rest-spread","transform-runtime"] }, }, { test: /\.less?$/, include: [path.resolve(__dirname, "./www/app")],//翻譯什麼文件夾 exclude: [path.resolve(__dirname, "node_modules")], //不翻譯什麼文件夾 use : [ {loader : 'style-loader'}, {loader : 'css-loader'}, {loader :'less-loader', options : { strictMath: true, noIeCompat: true } } ] } ] } }
樣式會被webpack編譯後出現在head標簽中
1.3提煉樣式表
extract-text-webpack-plugin這個插件主要是為了抽離css樣式,將樣式文件單獨打包(打包輸出的文件位置由webpack配置文件的output屬性指定)。防止將樣式打包在js中引起頁面樣式載入錯亂,然後在入口HTML頁面link標簽引入這個打包後的樣式文件即可。
下麵是插件的安裝方法:
https://www.npmjs.com/package/extract-text-webpack-plugin
npm install --save-dev extract-text-webpack-plugin 如果用的webpack 4代版本,請下載新版的: npm install --save-dev extract-text-webpack-plugin@next
webpack.config.js配置:
const path = require('path'); const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { entry: "./www/app/main", output : { path: path.resolve(__dirname, "www/dist"), filename : "bundle.js", }, watch:true, mode : "development", module: { rules: [ { test: /\.js?$/, include: [path.resolve(__dirname, "./www/app")], exclude: [path.resolve(__dirname, "node_modules")], loader : "babel-loader", options: { presets: ["env","react"], //要翻譯的語法 plugins: ["transform-object-rest-spread","transform-runtime"] }, }, { test: /\.less?$/, include: [path.resolve(__dirname, "./www/app")],//翻譯什麼文件夾 exclude: [path.resolve(__dirname, "node_modules")], //不翻譯什麼文件夾 use: ExtractTextPlugin.extract({ fallback: "style-loader", //編譯後用什麼loader來提取css文件 //指定什麼loader翻譯文件,這裡由於源文件時.less所以選擇less-loader use: ["css-loader","less-loader"] }) } ] }, plugins: [ new ExtractTextPlugin("styles.css"), ] }
如果遇見報錯,重新在項目依賴中安裝一下webpack 4代的版本即可
index.html引入打包後的css
<link rel="stylesheet" href="dist/styles.css">
二、antd組件庫
官網:https://ant.design/index-cn
文檔:https://ant.design/docs/react/introduce-cn
安裝依賴:
npm install --save antd
方法1:import引入樣式表
import 'antd/dist/antd.css';
缺點就是將antd的樣式表打包到dist/bundle.js目錄,合併到dist/style.css中,webpack會效率低。
方法2:去node_modules\antd\dist\antd.min.css找到文件,複製到www文件夾中的css文件夾中,在index.html引入即可。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <!-- <link rel="stylesheet" href="dist/styles.css"> --> <link rel="stylesheet" href="css/antd.min.css"> </head> <body> <div id="app"> </div> </body> <script type="text/javascript" src="dist/bundle.js"></script> </html>示例代碼
在使用antd的時候,所有的組件都是大寫字母開頭,比如按鈕
import {Button} from "antd" <Button></Button>
在App組件中小測:
import React from 'react'; import {connect} from "dva"; import "./App.less"; import PicShow from "../components/picshow/index.js"; import { Button ,DatePicker} from 'antd'; class App extends React.Component { constructor(props) { super(props); } onChange(date, dateString) { console.log(date, dateString); } render() { return ( <div className="app"> <Button type="primary">Primary</Button> <DatePicker onChange={this.onChange.bind(this)} /> </div> ); } } export default connect()(App);
三、React腳手架
在開發Reat應用時,大家都是webpack + es6結合React開發,手動用npm安裝各種插件,從頭到尾自己搭建環境,雖然搭建環境過程也是很好的學習過程,但有時難免遇到各種問題,而且每次開發新應用,都是要從頭搭建,未免太繁瑣。
於是,出現了腳手架,避免開發過程中重覆造輪子和做無用功,節省開發時間。
以前還要npm init一路擼到配置package.json、webpack.config.js文件,現在腳手架一行擼穿,懶人的世界如此美好。
React腳手架有很多,網上比較多人用和關註的,它們分別是:
create-react-app
react-boilerplate
react-redux-starter-kit
使用這三個腳手架的人都相當多,最突出的是create-react-app。
它是後來居上,他的開發時間都晚於後兩個,但關註量卻比他們還大,因為是facebook官方開發的。
當然,用得比較順手的腳手架是create-react-app和阿裡的Antd Design of React
3.1 create-react-app
安裝:
npm install -g create-react-app
安裝完成後,生成一個新的項目,使用下麵的命令:
create-react-app my-app
創建my-app目錄,然後噼里啪啦就OK啦!
npm start
預設情況下,會在開發環境啟動伺服器,監聽3000埠,會主動給你打開瀏覽器,可以立刻看到app的效果。
它會創建一個很純粹的React項目,安裝引用了以下依賴:
"dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "react-scripts": "1.1.1" }
沒有其他依賴,所以如果要自己加一些庫(UI組件什麼的),就要自己安裝配置了。
真的很清爽,所有的源碼你將放到src目錄下,什麼配置文件,各種亂七八糟都不用管,你只需要專註開發就好了,create-react-app都給你處理好了。
如果你使用過webpack-dev-server或webpack搭建過開發環境,你就會發現create-react-app的開發環境也有類似webpack-dev-server的--inline --hot自動刷新的功能。
我們探究一下是怎麼回事,難道create-react-app也用上了webpack-dev-server?
翻看了一下源碼,沒有找到webpack.config.js文件,如果有使用webpack就應該有這個文件,好奇怪。
看了一下node_modules目錄,也沒找到webpack相關的東西。
先源頭入手,我是用npm start命令來運行項目的。就從package.json文件入手,它的內容是這樣的:
"start": "react-scripts start"
react-scripts又是什麼?
在node_modules目錄中能找到它,它果然依賴了好多工具,其中就包括'webpack'。
裡面果然也有webpack的配置文件,也有好多腳本文件。
原來它是facebook開發的一個管理create-react-app服務的工具。
原來也是它讓整個源碼變得很整潔的。
因為它隱藏了沒必要的文件,大多數人的配置都是差不多的。
除此之外,它還加入了eslint的功能。讓你在開發過程中,更關註於代碼,很不錯。
3.2線上編譯命令
這個是create-react-app的一個大亮點,它能讓你的應用編譯出線上上生產環境運行的代碼,編譯出來的文件很小,且文件名還帶hash值,方便我們做cache,而且它還提供一個伺服器,讓我們在本地也能看到線上生產環境類似的效果,真的超級方便。
只需一行命令:
npm run build
四、父子組件之間數據傳遞(props)
多組件的世界。
如果App組件內引入了<Child>子組件,此時App組件叫Child組件的父組件,Child是App組件的子組件。
現在要研究的是:
父組件的數據,怎麼傳給子組件?
子組件能不能直接更改父組件的數據值?
4.1父親通過自定義屬性傳給子組件值,子組件通過this.props得到父親傳的值
App父組件中,可以給自定義標簽加自定義屬性,比如a={this.state.a},表示將a屬性傳給兒子。
App.js父組件
import React from "react"; import Child from "./components/Child"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } render() { return <div> <h1>我是父組件:{this.state.a}</h1> <Child a={this.state.a} ></Child> </div> } }
Child.js子組件:
import export default class Child extends React.Component { constructor() { super(); } render() { return <div> <h1>我是子組件:{this.props.a}</h1> </div> } }
4.2父組件中改變state的值,會引發子組件視圖更新
如果父組件將a屬性傳給子組件,此時父組件中對a的改變,會影響子組件接收的a值
import React from "react"; import Child from "./components/Child"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } render() { return <div> <h1>我是父組件:{this.state.a}</h1> <button onClick={()=>{this.setState({a:this.state.a+1})}}>改變a的值</button> <Child a={this.state.a} ></Child> </div> } }
4.3 props是只讀的
不能在子組件中直接改變父組件傳過來的state值:
import React from "react"; export default class Child extends React.Component { constructor() { super(); } render() { return <div> <h1>我是子組件:{this.props.a}</h1> <button onClick={() => { this.props.a++ }}>改變a的值</button> </div> } }
props是只讀的,會報錯:
4.4父親傳入函數,讓子組件改變父親的值
App父組件中必須提供一個向子組件暴露的函數:
import React from "react"; import Child from "./components/Child"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } setA(a){ this.setState({a}) } render() { return <div> <h1>我是父組件:{this.state.a}</h1> <button onClick={()=>{this.setState({a:this.state.a+1})}}>改變a的值</button> <Child a={this.state.a} setA={this.setA.bind(this)}></Child> </div> } }
import React from "react"; export default class Child extends React.Component { constructor() { super(); } render() { return <div> <h1>我是子組件:{this.props.a}</h1> <button onClick={() => { this.props.setA(this.props.a+1)}}>改變a的值</button> </div> } }
通過state來管理所有的數據。
總結:
1)傳<Child a={this.state.a}></Child> 收{this.props.a}
2)傳輸是單向的:父親能傳給兒子,父親改變值,兒子會render;但兒子不能改變只讀的props值;
3)如果非要改父親的值,父親必須傳一個函數<Child setA={this.setA.bind(this)}></Child>
兒子就能調用這個函數改變父親的state。
React極大簡化了組件之間的數據傳遞,也就是說,如果兄弟傳給兄弟,也要通過父親。
4.5調色板-案例
App組件
import React from "react"; import Bar from "./components/Bar"; export default class App extends React.Component{ constructor(){ super(); this.state = { r:100, g:200, b:255 } } //改變顏色的函數 setColor(k,v){ this.setState({ [k]:v }) } render(){ return <div> <div style = {{ "width":"200px", "height":"200px", "backgroundColor":`rgb(${this.state.r},${this.state.g},${this.state.b})` }}> </div> <Bar k="r" v={this.state.r} setColor={this.setColor.bind(this)}></Bar> <Bar k="g" v={this.state.g} setColor={this.setColor.bind(this)}></Bar> <Bar k="b" v={this.state.b} setColor={this.setColor.bind(this)}></Bar> </div> } };
Bar組件:
import React from 'react'; export default class Bar extends React.Component { constructor() { super(); } render() { return <div> <input type="range" max="255" value={this.props.v} onChange={(e)=>{this.props.setColor(this.props.k, e.target.value)}} /> <span>{this.props.v}</span> </div> } }
4.6購物車-案例
index.html
<style type="text/css"> .grid{ float: left; width:150px; border: 1px solid #000; line-height: 40px; } .bar{ overflow: hidden; } </style>示例代碼
App.js
import React from "react"; import Bar from "./components/Bar.js"; export default class App extends React.Component { constructor() { super(); this.state = { "arr" : [ {"id":1,"name":"空調", "price":3000, "amount":1}, {"id":2,"name":"電腦", "price":5000, "amount":1}, {"id":3,"name":"手機", "price":6000, "amount":1}, {"id":4,"name":"電視", "price":9000, "amount":1}, {"id":5,"name":"冰箱", "price":2000, "amount":1} ] } } //更改數量,根據id改變amount setAmount(id , amount){ //驗收,如果amount是小於0的,那麼什麼都不做 if(amount < 0){return} this.setState({ "arr" : this.state.arr.map(item=>{ //如果你遍歷的這個項的id不是要改的那個id,此時返回原來的項 //如果你遍歷的這個項的id是你要改變的項,此時返回改變的amount return item.id == id ? { ...item, amount } : item }) }); } render(){ return <div> { this.state.arr.map(item=>{ return <Bar key={item.id} item={item} setAmount={this.setAmount.bind(this)} ></Bar> }) } <h1> 總價: { this.state.arr.reduce((a,b)=>{ return a + b.price * b.amount; },0) } </h1> </div> } }
Bar.js組件:
import React from "react"; export default class Bar extends React.Component { constructor() { super(); } render() { //先將接受到的props解構出來 const {id, name, price, amount} = this.props.item; const setAmount = this.props.setAmount; return <div className="bar"> <div className="grid">{id}</div> <div className="grid">{name}</div> <div className="grid">{price}</div> <div className="grid"> <button onClick={()=>{setAmount(id, amount - 1)}}>-</button> {amount} <button onClick={()=>{setAmount(id, amount + 1)}}>+</button> </div> <div className="grid">小計:{amount * price}</div> </div> } }
每一個組件僅掌握了自己需要的數據,這樣的話每一個組件只需要專註於處理自己的這條數據即可。