当前位置:网站首页>Ficusjs series (I) introduction to ficusjs
Ficusjs series (I) introduction to ficusjs
2022-06-12 11:41:00 【Jioho_】
New toys are coming from once again at the front end
ficusjs series
List of articles
- ficusjs series
- ficusjs
- hello world
- introduce
- Create a component
- ficusjs - createComponent
- ficusjs - One must return one ** Can be passed to render** Function of the response of
- ficusjs - root Parsing
- ficusjs - props Parameters
- ficusjs - Computed
- ficusjs - State
- ficusjs - Method Method
- ficusjs - Life cycle
- ficusjs - renderer function
- ficusjs - Rendering Rendering mode
- ficusjs - The incident was distributed emit
- ficusjs - slot Slots
- Last
ficusjs
The old rules should be introduced first ficusjs. Document address :docs-ficusjs
say ficusjs Before , I have to say Web Components
Web Components Aimed at solving these problems — It consists of three main technologies , They can be used together to create custom elements that encapsulate functionality , It can be reused anywhere you like , Don't worry about code conflicts .
Before I understand , This is the next generation web The front-end technology , Let the Componentization of the front end be further ! At present, the front-end componentization is based on the framework ( such as vue,react And so on. ), and web components It provides natural isolation , And you can html User defined labels are written in the interface ! And there is also the component's own life cycle , The best thing is , After writing the custom label , Usage and div As like as two peas , It can only be said that it is better than ! Include document.querySelector You can also find the corresponding element . Compatibility is fairly good OK~ caniuse - web components
and ficusjs Is based on web components The next generation front-end framework for
hello world
No matter what you learn ,hello world Don't forget
Take the official website directly demo Let's see... First
<hello-world></hello-world>
<script type="module"> import {
html, renderer } from 'https://cdn.skypack.dev/@ficusjs/[email protected]/htm' import {
createComponent } from 'https://cdn.skypack.dev/[email protected]/component' createComponent('hello-world', {
renderer, handleClick() {
window.alert('Hello to you!') }, render() {
return html` <div> <p>FicusJS hello world</p> <button type="button" οnclick="${
this.handleClick}">Click me</button> </div> ` } }) </script>
Look at the effect :hello-world Labels are rendered directly ,( Unlike vue and react Finally, the code is converted into div)

introduce
Document address https://docs.ficusjs.org/installation/
ficusjs - CDN introduce
cdn The introduction of is particularly interesting , Have much deno The smell of
Pay attention to
type="module"To introduce . For lower versions , I won't support ittype="module"Browser , Or use it npm introduce , Use webpack pack
- All functions are introduced
<script type="module"> import {
// components createComponent, // extending components withStateTransactions, withStore, withEventBus, withStyles, withLazyRender, // event bus createEventBus, getEventBus, // app state createAppState, getAppState, createPersist, // stores - DEPRECATED createStore, getStore, // modules use } from 'https://cdn.skypack.dev/[email protected]' </script>
- Part of the introduction
import {
createComponent, use } from 'https://cdn.skypack.dev/[email protected]/component'
import {
withStateTransactions } from 'https://cdn.skypack.dev/[email protected]/with-state-transactions'
import {
withEventBus } from 'https://cdn.skypack.dev/[email protected]/with-event-bus'
import {
withStore } from 'https://cdn.skypack.dev/[email protected]/with-store'
import {
withStyles } from 'https://cdn.skypack.dev/[email protected]/with-styles'
import {
withLazyRender } from 'https://cdn.skypack.dev/[email protected]/with-lazy-render'
// The same is true for other modules ...
ficusjs - Use npm structure
npm install ficusjs
The rest of the introductions are similar
import {
createComponent, use } from 'ficusjs'
Create a component
From the most basic demo Starting with , See the introduction html、renderer、createComponent Method . So let's see how it works
ficusjs - createComponent
createComponent('hello-world', {
renderer,
handleClick() {
window.alert('Hello to you!')
},
render() {
return html` <div> <p>FicusJS hello world</p> <button type="button" οnclick="${
this.handleClick}">Click me</button> </div> `
}
})
The first thing to do is createComponent . receive 2 Parameters , One is the name of the component , The second parameter is some configuration items
The following table is from the translation of official documents ( Don't hit me if you can't translate )
| attribute | If required | type | describe |
|---|---|---|---|
| renderer | Required | function | A function , For rendering from render The function returns . |
| render | Required | function | One must return one Can be passed to render Function of the response of |
| root | string | Set the root definition of the component | |
| props | object | there props Just like vue It's very similar , Receive the parameters of the component | |
| computed | object | This and vue Of computed It's also very similar ! Used to return data | |
| state | function | A function that returns an object containing the initial state . State is an internal variable in a component ( and vue Of data The function is very similar to ) | |
| * | function | Any method in the component , Can be written here , And then through this.xxx call | |
| created | function | Life cycle - When the component is created , Before it is connected to DOM Called before | |
| mounted | function | Life cycle - DOM After mounting | |
| updated | function | Life cycle - When the component changes | |
| removed | function | Life cycle - When components are destroyed |
ficusjs - One must return one Can be passed to render Function of the response of
Say what this means ,render Function ,return html`` Pay attention to this html Not with () call , And directly connect the string type , Use html After processing , To pass on to renderer function
ficusjs - root Parsing
root It received a string Type of things . Altogether 3 It's worth :
| value | describe |
|---|---|
| standard | An ordinary html node |
| shadow | An open Shadow DOM node ( See the picture below ) |
| shadow:closed | A closed Shadow DOM node |

About Shadow DOM , I don't know much about , See for yourself ~ Shadow DOM
ficusjs - props Parameters
props The parameters of and vue It is also very similar , There is only one small difference
props: {
className: {
type: String,
default: 'btn',
required: true, // is this required?
observed: false // turn off observing changes to this prop
},
required: {
type: Boolean,
default: false
}
}
| attribute | If required | value |
|---|---|---|
| type | yes | This must be a string 、 Numbers 、 One of Boolean values or objects (String、Number、Boolean、Object) |
| default | If no default value is set , Set a default value | |
| required | When using this component , Whether this parameter is required (true/false) | |
| observed | Whether to listen for the change of this parameter , If set to false, Even if the parent component updates the value of the parameter , The component will not respond |
Q1: stay html In the code , How can I deliver Object object ?
Accidentally saw the source code , about Object Type of , Just do it simply JSON.parse Handle .

<!-- This code can -->
<hello-world user-info='{
"name":"Jioho"}'></hello-world>
<!-- This code doesn't work !! -->
<hello-world user-info='{name:"Jioho"}'></hello-world>
Because what you receive is not real Object object , therefore {name:"Jioho"} It can't go through JSON.parse Escaped
Q2:type Array does not seem to be supported in (Array)
Array types are loosely defined , It also belongs to the object type , So as long as we put the array JSON.stringify And then it's coming in , then type Set to Object You can receive the array object
Q3: stay html In the code , Parameter name using hump is not recognized
If you want to use in parameters userInfo This hump is named , So in html We need to use - To declare , namely user-info. But when defining parameters props and When getting value , Still use hump to get
<hello-world user-info='{
"name":"Jioho"}'></hello-world>
<script> // Omit a bunch of code console.log(this.props.userInfo) // {name:"Jioho"} </script>
Q4: Since it is props ,js How to modify these parameters , Whether it will respond immediately after modification ?
The parameters are reactive , The modification is effective immediately
Since the parameters are written directly to html Of , So the way to modify parameters and modify html The attribute mode of is consistent :
const helloWorld = document.querySelector('hello-world')
helloWorld.setAttribute('user-info', JSON.stringify({
name: 'Jioho2' }))
ficusjs - Computed
Compute properties ,emmm Or that sentence , and vue The effect is the same . Just the same , The function is completely different !!
- 【 Different 】ficusjs Of Computed and No dependency collection , As long as it is state perhaps props Of Parameter changes , He will re execute
- 【 identical 】ficusjs Of Computed There will also be a computing cache , As long as the parameters do not change , The calculated value is also taken when retrieving , No double counting
Prepared a little demo Under the demo :
<body>
<hello-world person-name="person-name" user-info='{
"name":"Jioho"}'></hello-world>
<button id="btn"> to update person-name</button>
<button id="btn_user"> to update user-info</button>
</body>
<script type="module"> import {
html, renderer } from 'https://cdn.skypack.dev/@ficusjs/renderers/lit-html' import {
createComponent } from 'https://cdn.skypack.dev/ficusjs/component' createComponent('hello-world', {
renderer, props: {
personName: {
type: String, required: true }, userInfo: {
type: Object, required: true } }, handleClick(e) {
console.log(this.props.userInfo, 'userInfo') console.log(this.myName, 'myName') }, computed: {
myName() {
console.log(' Trigger computed-myName') let propName = this.props.userInfo.name return propName + '_' } }, state() {
return {
count: 0 } }, render() {
return html` <div> <button type="button" @click="${
this.handleClick}">Click me!</button> </div> <div>${
this.props.userInfo.name}</div> <div>${
this.myName}</div> ` } }) document.getElementById('btn').onclick = function() {
document.querySelector('hello-world').setAttribute('person-name', ' Random ' + new Date().getTime()) } document.getElementById('btn_user').onclick = function() {
document.querySelector('hello-world').setAttribute('user-info', JSON.stringify({
name: 'Jioho222' })) } </script>
- The effect after the first run
This time it was triggered because html Used in the myName This attribute 
- Then update userInfo
After clicking the button , The page is really starting to update , Then it triggers again computed

- Click on to update person-name Button
difference : Updating person-name when , The code doesn't use person-name attribute , But computed It was triggered again , This sum vue Collection dependencies are a little different when triggered !

- Click on click me !
In the button click event , I've regained this.myName attribute , You can see computed There is no trigger , However, the corresponding value is also output , This sum vue The calculated cache of is a bit similar ~

Including the following changes demo, Output multiple names , It will only trigger the calculation amount once 
ficusjs - State
Page data ,react Also called state,vue Is used data Express .state It's also a function , then return Corresponding data , As for why we use functions , Blind guess a wave and vue The principle is similar vue Component's data Why must it be a function
{
state () {
return {
count: 0,
myName: 'state-myName',
list: [{
name: 'Jioho', age: 18 }, {
name: 'Jioho2', age: 20 }, {
name: 'Jioho3', age: 22 }]
}
}
}
// When used on the page, it is as follows :
// ${this.state.count}
Q1: How to reassign ?
The assignment has 2 Ways of planting , One is direct assignment , One is to use setState Method
- Direct assignment
Disadvantages of direct assignment :
- Page updates are asynchronous , If the subsequent steps depend on the effect of the assignment , That direct assignment is not suitable for
- Direct assignment cannot assign an item of an array (setState Not good either. )
this.state.count++
- Use setState function
setState The function is written in a strange way , And applets and react similar , But you can't say the same ~
- Support callback after assignment
- return The function only needs to return the value that needs to be updated , There is no need to return the entire state
- Note that the first argument must be a function , The return value in the function is what needs to be updated
this.setState(
state => {
let _list = state.list
_list[0].name = 'Jioho_update'
return {
list: _list
}
},
() => console.log(' Render completion callback ')
)
Q2: Must pass this.state.xxx Is the value taken ? this.xxx Whether the value can be ? Can and computed The same name ?
Must pass this.state.xxx Value ,this.xxx We can 't get the data . Because of this feature , and computed You can have the same name (vue You can't have the same name )
and computed The value specification is this.get.xxx The way to take value , Do not write after test .get It doesn't affect .
ficusjs - Method Method
There is nothing to say about the method , It's all written in the same way
ficusjs - Life cycle
{
created() {
console.log('created')
},
mounted() {
console.log('mounted')
},
updated() {
console.log('updated')
},
removed() {
console.log('removed')
}
}
There are few lifecycles
| name | Trigger time |
|---|---|
| created | After the node is created, it has not been mounted |
| mounted | Mount on the page |
| updated | Data trigger update ( The first mount will also trigger ) |
| removed | When components are removed |

created and mounted Between
This question is worth saying . When you see the document, you will find ,render Function support Promise As return value . That's like the following paragraph render It works , Guess what the life cycle is ?
render() {
return new Promise((resolve, reject) => {
console.log(' Began to run ', new Date())
setTimeout(() => {
resolve(html` <div> <p>FicusJS hello world</p> <button type="button" οnclick="${
this.handleClick}">Click me</button> <div>${
this.state.count}</div> </div> `)
console.log('resolve end', new Date())
}, 1000)
})
}
!! You're ready to see the answer !!

created stay JS After the resource is loaded, it starts to run , And then it goes into 1s The waiting for , Just executed mounted. It is worth saying that created It's better than render Function is executed earlier
updated Life cycle
As shown in the figure above , When no operation is done after the node is mounted , He also triggersupdated( Rendering static nodes also triggers updates )
removed Life cycle
document.querySelector('hello-world').remove()
ficusjs - renderer function
Maybe he has always been an abbreviation .renderer It's from JS Library introduced . stay createComponent Medium renderer Is a component similar to this one Callback function
createComponent('hello-world', {
renderer
})
// Equate to
createComponent('hello-world', {
renderer(what, where) {
console.log(what, where)
renderer(what, where)
}
})
You can also see from the screenshot ,renderer The implementation of is also in created and mounted Between , And in render Function returns before executing

The execution sequence of each update of the component's value :render function -> renderer function -> updated

say renderer Of the callback function 2 It's worth , and renderer Method
Notice which of the following saysCallback function. Which isrenderer Method
ficusjs - The callback function called back 2 Parameters what and where
- what yes
renderFunction to process the returned html structure , The above figure can also be seen in the print , Printed out is a dom node
therefore , At the node mounetd Before , perhaps update Before , You can forge a node !! Like the following
renderer(what, where) {
console.log(what, where)
let myDiv = document.createElement('div')
myDiv.innerText = 'hello world'
renderer(myDiv, where)
},
render() {
return html` <div> <p>FicusJS hello world</p> </div> `
})
}
although render Function returns FicusJS hello world . Content that can be last mounted to the node , yes hello world

- where Is the node to be mounted ( Here are some knowledge points !!)
The node to be mounted is easier to understand , By default, we are attached to the location where the label is written , Then we can specify other places on the interface . For example html Add one id=app The new node . The mount function is also changed , obtain app node , Then incoming renderer Function
<div id="app"></div>
<hello-world></hello-world>
renderer(what, where) {
console.log(what, where)
let myDiv = document.createElement('div')
myDiv.innerText = 'hello world'
let app = document.getElementById('app')
renderer(myDiv, app)
},
Yes ,myDiv The content of is mounted to #app in . Although the node has been transferred to #app, But the data is still responsive .

【 So here we go 】
Do you think it's over ? Remember remove Life cycle ? under these circumstances , We put hello-world Node removal
- Trigger removed Life cycle
- Click the button to update count -> render function ,updated And so on !
( I don't know bug Or features , Anyway, I dare not ask )


ficusjs - Rendering Rendering mode
With the top renderer Cognitive basis , Look again. Rendering It's easy to understand
Look at the document ( The main idea is , Handle html Support multiple rendering engines ,uhtml,lit-html,htm) And a series of rendering engines :

I just picked one htm Let's try
The introduction is as convenient as ever (npm If you want to introduce it, just look at the document )
// Before ficusjs Note out
// import { html, renderer } from 'https://cdn.skypack.dev/@ficusjs/[email protected]/htm'
// Replace with a new rendering engine ( Notice that this is render, instead of rendered 了 )
import {
html, render } from 'https://unpkg.com/htm/preact/standalone.module.js'
import {
createComponent } from 'https://cdn.skypack.dev/[email protected]/component'
html It hasn't changed , therefore render The function does not need to be changed , Note the following render Method , It's used htm The method of rendering
renderer(what, where) {
console.log(what, where)
render(what, where)
},
render() {
return html` <div> <p>FicusJS hello world</p> </div> `
})
}
The rendering result is exactly the same , The difference is what Namely render The data returned by the function , The previous return is html node , Now the virtual node is returned , And then through render Method to the corresponding node , After mounting to the page . The same is true for other rendering engine frameworks . There are also other framework features that have been explored by themselves

ficusjs - The incident was distributed emit
The incident is distributed with this.emit(eventName,eventData). Accept 2 Parameters (eventName and eventData)
- eventName The name of the dispatch event , Listening will also listen to the name
- eventData Data distributed , It's an object type . In the corresponding event e.detail The corresponding value can be obtained from the object
Look at the specific usage :
Attach a demo
<body>
<div id="app"></div>
<hello-world></hello-world>
<button id="btn">remove</button>
<script type="module"> import {
html, renderer } from 'https://cdn.skypack.dev/@ficusjs/[email protected]/htm' import {
createComponent } from 'https://cdn.skypack.dev/[email protected]/component' // Prepare a sub component createComponent('my-count', {
renderer, state() {
return {
count: 0 } }, addCount() {
this.state.count++ this.emit('changeCount', {
count: this.state.count }) }, render() {
return html` <p>${
this.state.count}</p> <button οnclick="${
this.addCount}">add count</button> ` } }) createComponent('hello-world', {
renderer(what, where) {
renderer(what, where) }, handleClick() {
this.state.count++ this.emit('updatecount', {
count: this.state.count, detail: {
name: 'Jioho' } }) }, root: 'shadow', state() {
return {
count: 0 } }, childrenChange(e) {
console.log(' The parent component listens for my-count', e) }, render() {
return html` <div> <p>FicusJS hello world</p> <button type="button" οnclick="${
this.handleClick}">Click me</button> <div>${
this.state.count}</div> <my-count onchangeCount="${
this.childrenChange}"></my-count> </div> ` } }) document.getElementById('btn').addEventListener('click', function() {
document.querySelector('hello-world').remove() }) document.querySelector('hello-world').addEventListener('updatecount', function(e) {
console.log('body monitor hello-world', e) }) </script>
</body>
Run effects and monitor clicks 
Say a few more important :
- emit After the event , If it is within the framework (createComponent Components of internal rendering ) Sure Direct use
on+ Event name monitor - If not within the framework , For example, the component is created in body node , At this time, we can't simply
<hello-world>Upper useonupdatecount. This is invalid
<!-- Invalid listening -->
<hello-world onupdatecount="updatecount"></hello-world>
<script> // It won't trigger here function updatecount(e) {
console.log(e) } // Valid binding document.querySelector('hello-world').addEventListener('updatecount', function(e) {
console.log('body monitor hello-world', e) }) </script>
ficusjs - slot Slots
The era of componentization , Slots are one of the essential features
- Default slot :
this.slots.default - A named slot :
this.slots.slotName(slotName) For custom name
// Omit a lot of code
return html` <div>${
this.slots.default}</div> <div>${
this.slots.button}</div> `
<hello-world>
<div> These are the contents for the default slot </div>
<button slot="actions"> Button of slot </button>
<div> These are the contents for the default slot 2</div>
</hello-world>
You can see ,slot=actions Is rendered to the specified location , The rest of the content is uniformly classified as default Slot for , If not used in the component this.slots.default. So the other 2 individual div I can't render it even if I write it 
Because I usually work with vue, Here are some small experiments and thoughts from my personal point of view
- Make complaints default slot

In the default slot , Put all the A newline , Space , etc. It's all recognized . comparison A named slot , It's specified div, This will be much better controlled .
By default, I have to perform a series of operations such as cyclic empty space determination , To get the child nodes I may want
The second is , Even if my default slot is empty , The label wraps After Formatting through the editor , The default slot will have a value ~ I feel it's easy to misjudge , In case any developer thinks that the default slot is [0] Is his node , Then hard coded this.slots.default[0].xxxx, Then the code of the next person to take over will be formatted and the label will wrap , that bug It's hard to check
- Slot cannot pass value
Sometimes the slot also wants to get some data in the component ,vue The words provide slot-scope , It is not impossible to do this in this framework , But it's not that simple .
For example, the picture above , Get the named slot ( Never use the default slot to do this , In this case, it is better to specify the name ), Use JS A native method , Set the attribute value of the label
render() {
this.slots.actions.setAttribute('slot-scope', JSON.stringify({
couunt: this.state.count }))
return html` <div>${
this.slots.default}</div> <div>${
this.slots.button}</div> `
}

In this way , If the contents of the slot are also custom components , That's right for me props . If not , It can only be used in the updated Periodic dispatch events , Notify the external business logic to perform corresponding operations .
Last
But compared with html Native 、web components It has been a great leap forward .ficusjs It is also a beginning stage , This is my study ficusjs The first note of , There are many useful features that will be updated in the future ~
hope web components Can develop quickly ,ficusjs And grow up quickly , So you don't have to think about it anymore vue still react 了 ~
边栏推荐
- C# 35. Select default network card
- Unity connect to Microsoft SQLSERVER database
- [Blue Bridge Cup SCM 11th National race]
- ARM指令集之杂类指令
- Clickhouse column basic data type description
- ioremap
- Blue Bridge Cup 2015 CA provincial competition (filling the pit)
- 如何查看glibc版本
- Record the pits encountered when using JPA
- Simple solution of regular expression
猜你喜欢

套接字实现 TCP 通信流程

Clj3-100alh30 residual current relay

C# 35. Select default network card

十折交叉验证代码中的问题

M-arch (fanwai 10) gd32l233 evaluation -spi drive DS1302

890. find and replace mode

Inter class and intra class relations in video classification -- regularization

人类想要拥有金钱、权力、美丽、永生、幸福……但海龟只想做一只海龟

conda环境下pip install 无法安装到指定conda环境中(conda环境的默认pip安装位置)

6.6 Convolution de séparation
随机推荐
A.前缀极差
判断网络文件是否存在,获取网络文件大小,创建时间、修改时间
MySQL45讲 01 | 基础架构:一条SQL查询语句是如何执行的?
UML series articles (30) architecture modeling -- product diagram
人类想要拥有金钱、权力、美丽、永生、幸福……但海龟只想做一只海龟
Socket programming UDP
[cf1392d] D. Omkar and bed Wars
[the 11th national competition of Blue Bridge Cup single chip microcomputer]
正则表达式 | 浅解
【蓝桥杯单片机 国赛 第十一届】
UI自动化测试中比较少见的异常记录
SharDingJDBC-5.1.0按月水平分表+读写分离,自动创表、自动刷新节点表
manuscript手稿格式准备
Arm cross compilation chain download address
Manuscript manuscript format preparation
Face recognition PIP failed to install Dlib Library
arm交叉编译链下载地址
Golang Foundation (7)
Golang基础(7)
【QNX Hypervisor 2.2 用户手册】4 构建QNX Hypervisor系统