快速入門 1. 創建第一個應用 由於該庫還不支持 src 引入,接下來的例子我將在 webpack 環境下演示,webpack 模板 已經配置完畢,可直接下載使用 創建一個應用可用通過 new 來創建實例或通過提供的 createApp 方法來創建下麵我將分別演示 通過 new 來創建 App , ...
快速入門
1. 創建第一個應用
由於該庫還不支持 src
引入,接下來的例子我將在 webpack
環境下演示,webpack 模板 已經配置完畢,可直接下載使用
創建一個應用可用通過 new
來創建實例或通過提供的 createApp
方法來創建下麵我將分別演示
- 通過
new
來創建 App ,el
配置項用來選擇DOM
被渲染到那個節點下,render
方法返回一個虛擬DOM
,h
函數可以創建一個虛擬DOM
,render
返回的虛擬DOM
將被轉換為真實DOM
並添加到 id 為 Root 的 節點下,!!! 通過new
創建的實例常常用來配置一些全局的方法,數據和組件
// 導入 Bindview.js
import Bindview from "bindview"
// new 一個實例
new Bindview({
el:"#Root",
render(h){
return h("div",{},["hello"])
}
})
- 通過
createApp
方法來創建 App , 在createApp
方法中傳入一個組件,在通過$mount
方法將虛擬DOM
添加到 id 為 Root 的 節點下,如何創建一個組件將在後面講到
import { createApp } from "bindview"
import App from "./App"
createApp(App).$mount("#Root")
2. 虛擬 DOM
的創建
- 通過
h
函數來創建虛擬DOM
, 虛擬DOM
本質上就是一個真實 DOM
抽象為一個 js 對象,對象上記錄了DOM
的類型,屬性和子節點的信息,在render
方法中可以通過h
函數來創建虛擬DOM
,h
通過下麵的方式都可以創建出虛擬DOM
, 通過h
的的函數來創建 虛擬DOM
的方式,不是很推薦,因為過程太繁瑣我推薦使用JSX
的方式來創建
// 導入 Bindview.js
import Bindview from "bindview"
new Bindview({
el: '#Root',
render(h) {
return h('ul', {}, [
h('li', {} , []),
h('li', null , [])
])
},
})
- 通過
JSX
來創建,在我配置好的 webpack 模板 下可以使用JSX
來創建虛擬DOM
,是非常推薦的做法,後面的例子都將使用JSX
的形式
// 導入 Bindview.js
import Bindview from "bindview"
new Bindview({
el: '#Root',
render() {
return (
<div>Hello World</div>
)
},
})
3. 組件的定義和使用
組件是 Bindview
中的一個重要概念,是一個可以重覆使用的 Bindview實例
,它擁有獨一無二的組件名稱,它可以擴展虛擬 DOM
,以組件名稱的方式作為自定義的虛擬 DOM
。因為組件是可復用的 Bindview 實例,所以它們與new Bindview()
接收相同的選項,例如data
, render(h){}
、methods
以及生命周期鉤子等。
把一些公共的模塊抽取出來,然後寫成單獨的的工具組件或者頁面,在需要的頁面中就直接引入即可。那麼我們可以將其抽出為一個組件進行復用。
定義一個組件,Bindview
的組件是一個函數返回一個配置對像,對象中只有 render
配置項是必須的其他的配置項都不是必須的, 函數的 props
形參 用來接收一些傳遞給組件的參數,組件中還有一些功能將在後面的內容中講到
// 定義一個 App 組件
export default function App(props) {
return {
name: 'App',
render() {
return (
<div id="App">
<div>Hello World</div>
</div>
)
}
}
}
使用組件, 將組件註冊到 components
配置項中,!!! 組件名一定要大寫,雖然可以小寫但這是為了避免一些錯誤,然後使用 JSX
方式來書寫就可以使用了
// 導入 Bindview.js
import Bindview from "bindview"
import App from "./App"
// new 一個實例
new Bindview({
el:"#Root",
render:() => (<App />),
components:{ App }
})
4. data
配置項和數據響應式
Bindview
使用了一種類似於 MVVM 的設計模式,數據和視圖進行綁定,頁面的顯示結果將受 data
配置項中的數據影響,而 data
配置項中的數據使用了 Vue3
數據代理的方法,修改 data
中的數據,視圖將自動更新
在下麵這個例子中,data
中配置一個 num
是數據,在 render
方法中 this
可以獲取到整個組件是實例,我們通過 this
解構出 data
並重命名為 $
,在 JSX
中使用數據,在 button
上綁定了一個點擊事件來對數據進行自增,當點擊 頁面上的 button 按鈕時頁面上的數據將自動更新
export default function () {
return {
name: 'App',
render() {
const { data: $ } = this
return (
<div id="App">
<div>{$.num}</div>
<button onClick={() => $.num++}>Button</button>
</div>
)
},
data:()=>({
num: 0
})
}
}
5. methods
配置項
methods
配置項用來定義一些組件內部需要使用的方法, 下麵的例子中在 methods
中定義了一個 Add
方法來對 data
中的 num
進行自增,在 onClcik
事件綁定中使用當點擊 button
時將調用該方法,Add
方法的 this
指向組件實例, !!! 事件綁定中的方法或函數會接收到兩個參數 第一個是事件綁定的 DOM
元素,第二個是事件對象 event
, *** 如果組件需要使用一些方法和函數這並不是唯一的方式
export default function () {
return {
name: 'App',
render() {
const { data: $,methods: f } = this
return (
<div id="App">
<div>{$.num}</div>
<button onClick={f.Add}>Button</button>
</div>
)
},
data: {
num: 0
},
methods:{
Add(){
this.data.num++
}
}
}
}
6. ref
獲取 DOM
ref
被用來給元素註冊引用信息。引用信息將會註冊在父組件的 refs
對象上。在 DOM
元素上使用,引用指向的就是 DOM
元素, ref
可以傳入一個 字元串
或一個 函數
,傳入字元串的 DOM
元素將引用到 refs
上,傳入函數函數會接收的 DOM
元素,在組件上使用 ref
只能使用傳入函數,函數接收的組件實例
export default function () {
let dom // 接收 dom 元素的實例
return {
name: 'App',
render() {
return (
<div id="App">
<div ref="div">hello</div>
<div ref={_dom => dom = _dom}>world</div>
</div>
)
},
life:{
createDom(){
console.log(this.refs) // { div:HTMLDivElement }
}
}
}
}
如果使用了相同的 ref
信息那麼,refs
對象中該信息將是一個數組保存了使用相同信息的 DOM
export default function () {
return {
name: 'App',
render() {
return (
<div id="App">
<div ref="box">hello</div>
<div ref="box">world</div>
</div>
)
},
life:{
createDom(){
console.log(this.refs) // { box:[ HTMLDivElement , HTMLDivElement ] }
}
}
}
}
7. linkage
聯動更新
在 Bindview
中當父組件中觸發更新時 ( 一般是 data
中的數據發生改變 ) 父組件會觸發所有後代組件的更新,因為父組件不知道那些後代組件使用了它自身的一些數據,所以他會觸發所有後代組件的更新方法,如果後代組件是視圖模型中發生了變化,那麼組件將更新視圖,但在開發過程中一些組件只做展示效果那麼可以通過 linkage
配置項來關閉父組件對自身的聯動更新,*** 註意 linkage
只會關閉父組件對子組件的聯動更新,而不會關閉子組件自身的數據響應式
下麵的例子中給 Son
子組件配置了 linkage
配置項當改變父組件中的 num
的值時,子組件將不會更新來獲取最新的數據來更新視圖
function Son(props) {
const { num } = props
return {
name: 'Son',
linkage: false,
render() {
return (
<div>{num()}</div>
)
}
}
}
export default function () {
return {
name: 'Dome',
render() {
const { data: $ } = this
return (
<div>
<button onClick={() => $.num++}>num++</button>
<Son num={() => $.num} />
</div>
)
},
data:()=>({
num: 0
}),
components: { Son }
}
}
8. 插槽
插槽是組件中不確定的部分由用戶來定義,這部分就叫插槽,相當於一種占位符,在組件中通過組件函數的 slot
來獲取插槽, slot
函數將返回插槽的虛擬 DOM
,在 render
中直接使用即可
在 Bindview
中組件插槽有兩種一種是 普通插槽
還有一種是 函數插槽
,下麵將分別說明每種插槽的作用
1. 普通插槽
普通插槽就是在組件中直接書寫文檔結構這種組件的特點就是在插槽中會失去響應式,也就是說在插槽中使用了響應式數據將無法獲得更新,一般用來展示一次性數據
function Son(props,slot) {
const { num } = props
return {
name: 'Son',
render() {
return (
<div>
{slot()} {num()}
</div>
)
}
}
}
export default function () {
return {
name: 'Dome',
render() {
const { data: $ } = this
return (
<div>
<button onClick={() => $.num++}>num++</button>
<Son num={() => $.num}>
<span>Num: </span>
</Son>
</div>
)
},
data: {
num: 0
},
components: { Son },
}
}
2. 函數插槽
函數插槽就是在組件中使用一個函數將文檔結構返回出去,這種方式將不會失去數據的響應式,同時它還可以拿到組件中的一些數據在 Son
組件中向 slot
傳遞一個字元串,在 Dome
組件中定義插槽的函數中可以通過 title
拿到並使用
function Son(_,slot) {
return {
name: 'Son',
render() {
return (
<div>
{slot("Num: ")}
</div>
)
}
}
}
export default function () {
return {
name: 'Dome',
render() {
const { data: $ } = this
return (
<div>
<button onClick={() => $.num++}>num++</button>
<Son>{(title) => (
<span>{title} {$.num}</span>
)}</Son>
</div>
)
},
data:()=>({
num: 0
}),
components: { Son },
}
}
3. 多插槽
在組件中可以使用多個插槽,在使用多插槽時需要使用 { }
雙花括弧包裹,每個單獨插槽就使用一個 {}
包裹 ,slot
將得到一個數組數組中包含了每個插槽
function Son(_,slot) {
return {
name: 'Son',
render() {
return (
<div>
<div>插槽1 {slot[0]()}</div>
<div>插槽2 {slot[1]()}</div>
</div>
)
}
}
}
export default function () {
return {
name: 'Dome',
render() {
return (
<div>
<Son>
{<span>多插槽1</span>}
{<span>多插槽2</span>}
</Son>
</div>
)
},
components: { Son },
}
}
9. proto 向原型添加屬性或方法
使用 proto
方法可以向構造函數的原型上添加屬性或方法,在創造實例前調用使用,有兩種使用方法,第一種每次只能添加一個方法或屬性,第二種使用對象形式可以添加多個方法或屬性,
import Bindview from "../../bindview"
// 使用一
Bindview.proto('Test', function(){
console.log("Test")
})
// 使用二
Bindview.proto({
Test1:"hello",
Test2(){
console.log("Test")
}
})
new Bindview({
el: '#Root',
render(h) {
return (
<div>hello</div>
)
}
})
10.插件
插件通常用來為 Vue 添加全局功能
插件用來給 bindview
拓展功能,如添加全局組件或全局屬性,通過 use
方法來使用插件, 傳入數組可以使用多個插件
!!! 插件需要是一個函數或一個帶有 _install_
方法的對象,它們會獲得 bindview
的構造器
import { Bindview } from "bindview@3"
import App from "./App";
import { history } from "bindview-router"
Bindview.use(history)
new Bindview({
el: '#Root',
render: () => (<App />),
components: { App }
})
11.全局組件
components
方法用來註冊全局組件,在 new Bindview
之前全局註冊的組件無需再註冊即可使用
import Bindview from "../../bindview@3"
import App from "./App";
Bindview.components("App",App) // 方式一
Bindview.components({ App }) // 方式二
new Bindview({
el:"#Root",
render: () => (<App />),
})
12. 動態組件
動態組件是在同一個位置根據不同的狀態顯示不同的組件,在動態組件中必須要有一個 id
參數並傳入一個不會改變的唯一 id
下麵是一個簡單的動態組件例子
import { crateId } from "bindview"
import Dome1 from "./Dome1"
import Dome2 from "./Dome2"
export default function (props) {
let { state } = props
const [ a , b ] = crateId(2)
return {
name: 'Test',
render() {
return state() ? <Dome1 id={a} /> : <Dome2 id={b} />
},
components: { Dome1, Dome2 }
}
}
生命周期鉤子
每個 Bindview
實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、將實例掛載到 DOM 併在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做 生命周期鉤子 的函數,這給了用戶在不同階段添加自己的代碼的機會
在 Bindview
中 生命周期鉤子 需要配置到 life
配置項中
生命周期鉤子 | 調用時 |
---|---|
beforeInit |
實例初始化前 |
created |
dom 創建後 |
updated |
數據改變後 |
beforeDestroy |
組件銷毀前 |
1. beforeInit
beforeInit
會在組件實例初始化時調用, 在該階段可以通過一個形參獲取到組件的配置對象通過修改配置對象可以修改最後被創建出來的實例,也可以在此階段通過 this
向組件實例上添加一些自定義的方法或數據
function Life() {
return {
name: "Lifecomponents",
render() {
return (
<div>hello</div>
)
},
life: {
beforeInit(ConfigObj){
console.log(ConfigObj)
}
}
}
}
2. created
created
鉤子會在 render
配置項中的 虛擬DOM 被創建為 真實DOM 後調用,在此階段就可以通過 refs
得到 DOM
元素了
function Life() {
return {
name: "Lifecomponents",
render() {
return (
<div ref="box">hello</div>
)
},
life: {
created(){
console.log(this.refs['box'])
}
}
}
}
3. updated
updated
鉤子會在 data
配置項中的數據被修改後調用
function Life() {
return {
name: "Lifecomponents",
render() {
return (
<div>hello</div>
)
},
life: {
updated(){
console.log("date發生改變")
}
}
}
}
4. beforeDestroy
beforeDestroy
鉤子會在組件被卸載之前調用,在此階段可以註銷事件監聽,和清空定時器
function Life() {
return {
name: "Lifecomponents",
render() {
return (
<div>hello</div>
)
},
life: {
beforeDestroy(){
// 清空定時器或註銷事件監聽
}
}
}
}
原型方法
在 Bindview
組件實例中有一些以 $
開頭的原型方法,這些方法提供了一些方便開發的功能,這些方法中有些一般情況下只是用一次應為這些方法一般是是用來給組件添加一些配置的 如 添加全局組件,下麵將說明每個原型方法的作用和使用方法
1. $appendComponent
$appendComponent
追加組件,向已經創建好的組件中註冊新的組件在 created
生命周期中調用,一般來註冊非同步請求後的組件
export default function App() {
return {
name: 'App',
render() {
const { data: _ } = this
return (
<div id="App">
{_.show?<div>占位<div>:<Test id="UUID" />}
</div >
)
},
data: () => ({
show: false
}),
life: {
async created() {
let module=await import("@/components/Test")
this.$appendComponent("Test",module.default)
this.data.show=true
}
}
}
}
2. $mount
$mount
安裝組件, 將組件實例安裝到頁面上,方法需要傳入一個 DOM 選擇字元串 ( '#Root' , '.Root' 等) 或是一個 DOM 元素
import { createApp,Bindview } from "../../bindview";
import App from "./App"
createApp(App).$mount("#Root")
// new Bindview({
// render: () => (<App />),
// components: { App }
// }).$mount("#Root")
3. $remove
$remove
卸載組件,通過組件實例調用可卸載組件,卸載時會調用 beforeDestroy
生命周期鉤子
import { createApp } from "../../bindview";
import App from "./App"
const vm = createApp(App).$mount("#Root")
// 卸載組件
vm.$remove()
4. $mupdate
$mupdate
方法可以手動更新視圖,在修改一些沒有數據響應的數據但需要更新視圖時可以在修改後調用這個方法,或傳入一個函數,視圖更新會在回調函數執行完後
export default function () {
return {
name: 'App',
render() {
const { methods: f } = this
return (
<div ref="Box" className="App">
App
<div>{f.datas()}</div>
<button onClick={f.addData}>addDatas</button>
</div>
)
},
methods: {
addData() {
this.$mupdate(() => {
this.datas++
})
},
datas() {
return this.datas
}
},
life: {
beforeInit() {
this.datas = 0
}
}
}
}
工具函數
在 Bindview
中不止有 Bindview
構造函數還提供了一些便於開發的工具函數
1. send
send
方法簡化組件間傳遞參數的方法,該方法需要傳入兩個參數 1.數據源 2.數據項,該方法會返回一個對象對象中有兩個方法分被是get
set
用來獲取和修改數據
import { Bindview, send } from "bindview"
// Dome 組件
function Dome(props) {
let { num, arr } = props
return {
name: 'Dome',
render() {
const { methods:f } = this
return (
<div>
<div>Dome</div>
<div>{num.get()}</div>
<button onClick={f.set}>set</button>
<div>{arr.get()}</div>
<button onClick={f.setArr}>setArr</button>
</div>
)
},
methods: {
set() {
// 1. num.set(100)
// 2. i 是數據
num.set(i => {
return ++i;
})
},
setArr() {
arr.set(100)
}
}
}
}
export default function () {
return {
el: '#Root',
render() {
const { data: $ } = this
return (
<div>
<div>App</div>
<Dome num={send($, 'num')} arr={send($.arr, 1)} />
</div>
)
},
data: {
num: 0,
arr: [1, 2, 3, 4]
},
components: { Dome }
}
}
2. createId
createId
函數用來創建多個唯一的 ID
, 函數傳入一個數值返回一個長度為該數值的數組數組中包含了唯一 ID
,不傳入將返回一個唯一的 ID
,一般配合動態組件使用
import { crateId } from "bindview"
import Dome1 from "./Dome1"
import Dome2 from "./Dome2"
export default function (props) {
let { state } = props
const [ a , b ] = crateId(2)
return {
name: 'Test',
render() {
return state() ? <Dome1 id={a} /> : <Dome2 id={b} />
},
components: { Dome1, Dome2 }
}
}
3. createApp
createApp
用來創建組件實例,創建的組件實例需要通過 $mount
進行掛載, createApp
可以傳入兩個參數第一個參數是組件函數是必須的,第二個是 props
對象不是必須的,這個對象在組件函數的 props
可以得到
import { createApp } from "../../bindview";
import App from "./App"
createApp(App, { title: '這是props' }).$mount("#Root")