1、前端開發的演變
到目前爲止,前端的開發經歷了四個階段,目前處於第四個階段。這四個階段分別是:
階段一:靜態頁面階段
在第一個階段中前端頁面都是靜態的,所有前端代碼和前端數據都是後端生成的。前端只是純粹的展示功能,js腳本的作用只是增加一些特殊效果,比如那時很流行用腳本控制頁面上飛來飛去的廣告。
那時的網站開發,採用的是後端 MVC 模式。
- Model(模型層):提供/保存數據
- Controller(控制層):數據處理,實現業務邏輯
- View(視圖層):展示數據,提供用戶界面
前端只是後端 MVC 的 V。
階段二:ajax階段
2004年,A JAX 技術誕生,改變了前端開發。Gmail 和 Google地圖這樣革命性的產品出現,使得開發者發現,前端的作用不僅僅是展示頁面,還可以管理數據並與用戶互動。
就是從這個階段開始,前端腳本開始變得複雜,不再僅僅是一些玩具性的功能。
階段三:前端MVC階段
2010年,第一個前端 MVC 框架 Backbone.js 誕生。它基本上是把 MVC 模式搬到了前端,但是隻有 M (讀寫數據)和 V(展示數據),沒有 C(處理數據)。
有些框架提出了MVVM模式,用 View Model 代替 Controller。Model 拿到數據以後,View Model 將數據處理成視圖層(View)需要的格式,在視圖層展示出來。
階段四:SPA階段
前端可以做到讀寫數據、切換視圖、用戶交互,這意味着,網頁其實是一個應用程序,而不是信息的純展示。這種單張網頁的應用程序稱爲 SPA(single-page-application)。
2010年後,前端工程師從開發頁面(切模板),逐漸變成了開發“前端應用”(跑在瀏覽器裏面的應用程序)。
目前,最流行的前端框架 Vue、Angular、React 等等,都屬於 SPA 開發框架。
2、ReactJS簡介
官方一句很簡單的話,道出了什麼是ReactJS,就是,一個用於構建用戶界面的JavaScript框架,是Facebook開發的一款的JS框架。
ReactJS把複雜的頁面,拆分成一個個的組件,將這些組件一個個的拼裝起來,就會呈現多樣的頁面。ReactJS可以用於 MVC 架構,也可以用於 MVVM 架構,或者別的架構。
ReactJS圈內的一些框架簡介:
- Flux
- Flux是Facebook用戶建立客戶端Web應用的前端架構,它通過利用一個單向的數據流補充了React的組合視圖組件,這更是一種模式而非框架。
- Redux
- Redux 是 JavaScript狀態容器,提供可預測化的狀態管理。Redux可以讓React組件狀態共享變得簡單。
- Ant Design of React
- 阿里開源的基於React的企業級後臺產品,其中集成了多種框架,包含了上面提到的Flux、Redux。
- Ant Design提供了豐富的組件,包括:按鈕、表單、表格、佈局、分頁、樹組件、日曆等。
3、搭建環境
3.1、創建項目
我們依然選擇使用UmiJS作爲構建工具。
創建工程:
輸入命令,進行初始化:
tyarn init -y
在命令輸入如下命令:
tyarn add umi --dev #項目中添加umi的依賴
可以看到,相關的依賴已經導入進來了。
3.2、編寫HelloWorld程序
第一步,在工程的根目錄下創建config目錄,在config目錄下創建config.js文件。
在UmiJS的約定中,config/config.js將作爲UmiJS的全局配置文件。 在umi中,約定的目錄結構如下:
├─ dist/ // 默認的 build 輸出目錄
├─ mock/ // mock 文件所在目錄,基於 express
├─ config/
├─ config.js // umi 配置,同 .umirc.js, 二選一
├─ src/ // 源碼目錄,可選
├─ layouts/index.js // 全局佈局
├─ pages/ // 頁面目錄,裏面的文件及路由
├─ .umi/ // dev 臨時目錄,需添加到 .gitignore
├─ .umi-production/ // build 臨時目錄,會自動刪除
├─ document.ejs // HTML 模板
├─ 404.js // 404 頁面
├─ page1.js // 頁面1,任意命名,到處 react 組件
├─ page1.test.js // 用例文件,umi test 會匹配所有 .test.js 和 .e2e.js 結尾文件
├─ page2.js // 頁面2,任意命名
├─ global.css // 約定的全局樣式文件,自動引入,也可以用global.less
├─ global.js // 可以在這裏加入 polyfill
├─ .umirc.js // umi 配置,同 config/config.js, 二選一
├─ .env // 環境變量
├─ package.json
在config.js文件中輸入以下內存,以便後面使用:
//導出一個對象,暫時設置爲空對象,後面再填充內容
export default {};
第二步,創建HelloWorld.js頁面文件
在umi中,約定存放頁面代碼的文件夾是在src/pages,可以通過singular:false來設置單數的命名方式,我們採用默認即可。
在HelloWorld.js文件中輸入如下內容:
export default () => {
return <div>hello world</div>;
}
在這裏,可以會比較奇怪,怎麼可以在js文件中寫html代碼,其實,這是react自創的寫法,叫JSX,後面我們再細說。
第三步,啓動服務查看頁面效果
#啓動服務
umi dev
可以看到,通過/HelloWorld路徑即可訪問到剛剛寫的HelloWorld.js文件。
在 umi 中,可以使用約定式的路由,在 pages 下面的 JS 文件都會按照文件名映射到一個路由,比如上面這個例子,訪問 /helloworld 會對應到HelloWorld.js。
當然了,也可以自定義路由,具體的路由配置在後面講解。
3.3、添加umi-plugin-react插件
umi-plugin-react插件是umi官方基於react封裝的插件,包含了13個常用的進階功能。
具體可查看:https://umijs.org/zh/plugin/umi-plugin-react.html
#添加插件
tyarn add umi-plugin-react --dev
添加成功:
接下來,在config.js文件中引入該插件:
export default {
plugins: [
['umi-plugin-react', {
//暫時不啓用任何功能
}]
]
};
3.4、構建和部署
現在我們寫的js,必須通過umi先轉碼後才能正常的執行,那麼我們最終要發佈的項目是普通的html、js、css,那麼應該怎麼操作呢?
其實,通過umi是可以進行轉碼生成文件的,具體操作如下:
umi build
可以看到,已經生成了index.html和umi.js文件。我們打開umi.js文件看看。
首先,看到的是umi.js文件是一個已經壓縮過的文件,然後搜索“hello world”,可以找到,我們剛剛寫的代碼已經被轉碼了。
至此,開發環境搭建完畢。
4、React快速入門
4.1、JSX語法
JSX語法就是,可以在js文件中插入html片段,是React自創的一種語法。
JSX語法會被Babel等轉碼工具進行轉碼,得到正常的js代碼再執行。
使用JSX語法,需要2點注意:
- 所有的html標籤必須是閉合的,如:
<div>hello world</div>
# 寫成這樣是不可以的:
<div>hello world
- 在JSX語法中,只能有一個根標籤,不能有多個。
const div1 = <div>hello world</div> //正確
const div2 = <div>hello</div> <div>world</div> //錯誤
SX語法中,如果想要在html標籤中插入js腳本,需要通過{}插入js腳本。
export default () => {
function name() {
return "李四";
}
// 引用js腳本 使用 { } 插入腳本
return <div>hello world {name()}</div>;
}
4.2、組件
件是React中最重要也是最核心的概念,一個網頁,可以被拆分成一個個的組件, 像這樣: React中,這樣定義一個組件:
import React from 'react'; //第一步,導入React
class HelloWorld extends React.Component { //第二步,編寫類並且繼承 React.Component
render(){ //第三步,重寫render()方法,用於渲染頁面
return <div>hello world!</div> //JSX語法
}
}
export default HelloWorld; //第四步,導出該類
查看效果:
4.2.1、導入自定義組件
創建Show.js文件,用於測試導入組件:
import React from 'react'
import HelloWorld from './HelloWorld' //導入HelloWorld組件
class Show extends React.Component {
render() {
return <HelloWorld/>; //使用HelloWorld組件
}
}
export default Show;
測試:
4.2.2、組件參數
組件是可以傳遞參數的,有2種方式傳遞,分別是屬性和標籤包裹的內容傳遞,具體使用如下,修改Show.js:
import React from 'react'
import HelloWorld from './HelloWorld' //導入HelloWorld組件
class Show extends React.Component {
render() {
return <HelloWorld name="zhangsan">shanghai</HelloWorld>; //使用HelloWorld組件
}
}
export default Show;
其中,name="zhangsan"就是屬性傳遞,shanghai就是標籤包裹的內容傳遞。
那麼,在HelloWord.js組件中如何接收參數呢?
對應的也是2種方法:
- 屬性:this.props.name 接收;
- 標籤內容:this.props.children 接收;
使用如下,修改HelloWorld.js:
import React from 'react'; //第一步,導入React
class HelloWorld extends React.Component { //第二步,編寫類並且繼承 React.Component
render(){ //第三步,重寫render()方法,用於渲染頁面
return <div>hello world! name={this.props.name}, address= {this.props.children}</div> //JSX語法
}
}
export default HelloWorld; //第四步,導出該類
測試:
4.2.3、組件的狀態
每一個組件都有一個狀態,其保存在this.state中,當狀態值發生變化時,React框架會自動調用render()方法,重新渲染頁面。
其中,要注意兩點:
一: this.state值的設置要在構造參數中完成;
二:要修改this.state的值,需要調用this.setState()完成,不能直接對this.state進行修改;
下面通過一個案例進行演示,這個案例將實現:通過點擊按鈕,不斷的更新this.state,從而反應到頁面中。
新建一個List.js 文件:
import React from 'react'
class List extends React.Component {
constructor(props) { // 構造參數中必須要props參數
super(props); // 調用父類的構造方法
this.state = { // 初始化this.state
dataList: [1, 2, 3],
maxNum: 3
};
}
render() {
return (
<div>
<ul>
{
// 遍歷值
this.state.dataList.map((value, index) => {
return <li key={index}>{value}</li>
})
}
</ul>
<button
onClick={() => { //爲按鈕添加點擊事件
let maxNum = this.state.maxNum + 1;
let list = [...this.state.dataList, maxNum];
this.setState({ //更新狀態值
dataList: list,
maxNum: maxNum
});
}}>
添加
</button>
</div>
);
}
}
export default List;
初始化狀態:
當點擊“添加”按鈕:
過程分析:
4.2.4、生命週期
組件的運行過程中,存在不同的階段。React爲這些階段提供了鉤子方法,允許開發者自定義每個階段自動執行的函數。這些方法統稱爲生命週期方法(lifecycle methods)。
生命週期示例:
import React from 'react'; //第一步,導入React
class LifeCycle extends React.Component {
constructor(props) {
super(props);
//構造方法
console.log("constructor()");
}
componentDidMount() {
//組件掛載後調用
console.log("componentDidMount()");
}
componentWillUnmount() {
//在組件從 DOM 中移除之前立刻被調用。
console.log("componentWillUnmount()");
}
componentDidUpdate() {
//在組件完成更新後立即調用。在初始化時不會被調用。
console.log("componentDidUpdate()");
}
shouldComponentUpdate(nextProps, nextState) {
// 每當this.props或this.state有變化,在render方法執行之前,就會調用這個方法。
// 該方法返回一個布爾值,表示是否應該繼續執行render方法,即如果返回false,UI 就不會更新, 默認返回true。
// 組件掛載時,render方法的第一次執行,不會調用這個方法。
console.log("shouldComponentUpdate()");
}
render() {
return (
<div>
<h1>React Life Cycle!</h1>
</div>
);
}
}
export default LifeCycle;
測試結果:
源代碼獲取
本文首發於公衆號「曉明的30天實驗室」,微信公衆號裏回覆 11299 獲取源代碼地址;
如果你覺得文章還不錯,請大家點贊分享下。你的肯定是我最大的鼓勵和支持。