• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

mvvm-reactive-view: 面向未来的,轻量级,响应式,mvvm,构建视图,声明式,组件化,基于web ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

mvvm-reactive-view

开源软件地址:

https://gitee.com/masx200/mvvm-reactive-view

开源软件介绍:


mvvm-reactive-view

这是一个实验性项目,此代码库仅供学习交流使用

面向未来的,轻量级,响应式,mvvm,构建视图,声明式,组件化,基于 webcomponent ,基于虚拟 dom

基于 Proxy,支持 jsxhyperscript,前端 javascript 库,完全使用TypeScript编写

虽然使用了虚拟dom,但是,与reactvue等之类的前端框架有本质上的不同,不使用diff算法,响应式状态直接与元素绑定,高效更新,性能更强

不使用 diff 算法,使用 proxy 精准监听状态变化,高效更新视图,状态都是响应式,可观察的对象,每次状态改变不会重新生成虚拟 dom,可实现最小化DOM操作

使用响应式状态管理全局共享状态,抛弃 redux,vuex,mobx,响应式状态可以独立于组件存在

有着面向未来的函数式API,提供与React Hooks相同级别的逻辑组合功能,方便复用逻辑和重用,抛弃mixin(混入)和hoc(高阶组件),Render Props

支持组件局部css样式,仅在组件内有效,不会全局生效

由于使用了 Proxy,所以不支持 IE 浏览器,而且 Proxy 不可 polyfill

兼容的浏览器

浏览器要求原生支持ProxyECMASCRIPT2017以上

EDGE,CHROME,FIREFOX,SAFARI

安装 npm 模块

cnpm install --save  https://github.com/masx200/mvvm-reactive-view.git

或者

yarn add https://github.com/masx200/mvvm-reactive-view.git

cdn 获取模块

开发模式

https://cdn.jsdelivr.net/gh/masx200/mvvm-reactive-view@latest/dist/index.js

生产模式

https://cdn.jsdelivr.net/gh/masx200/mvvm-reactive-view@latest/dist/index.min.js

关于 Proxy

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

polyfill

import "@masx200/mvvm-reactive-view/dist/polyfill.js";
<script src="https://cdn.jsdelivr.net/gh/masx200/mvvm-reactive-view@latest/dist/polyfill.js"></script>

需要webcomponent custom-elements polyfill

https://github.com/webcomponents/polyfills/tree/master/packages/custom-elements

ECMAScript2019 polyfill

由于使用了 ECMAScript2019api,所以需要自行添加 polyfill

需要 Object.fromEntriesArray.prototype.flat的 polyfill

https://github.com/zloirock/core-js

https://github.com/tc39/proposal-object-from-entries

https://tc39.es/proposal-flatMap/

使用 npm 模块

import {    Switchable,    computed,    createComponent,    useCreated,    useUpdated,    useMounted,    useUnMounted,    Condition,    Directives,    watch,    html,    h,    MountElement,    createRef,    createElement,    createState,    render} from "@masx200/mvvm-reactive-view";

快速上手,可在浏览器中运行而不需要编译工具

index.js

import {    Switchable,    computed,    createComponent,    useCreated,    useUpdated,    useMounted,    useUnMounted,    Condition,    Directives,    watch,    html,    h,    MountElement,    createRef,    createElement,    createState} from "https://cdn.jsdelivr.net/gh/masx200/mvvm-reactive-view@latest/dist/index.min.js";const inputref = createRef();const state1 = createState("hello");const stylestate = createState({ display: "block", width: "700px", color: "" });const vdom = html`    <div style=${{ display: "block", width: "500px" }}>hello world!</div>    <input        style="width:800px"        @input=${(e) => (state1.value = e.target.value)}        *ref=${inputref}        @change=${(e) => (state1.value = e.target.value)}        id="code16"        class="col-lg-12 col-md-12 col-sm-12 col-xs-12 snippet code16d form-control"        value=${state1}    />    <h1 style=${stylestate}>mvvm-reactive-view</h1>    <button        @click=${() => {            stylestate.color = "red";        }}    >        red    </button>    <button        @click=${() => {            stylestate.color = "green";        }}    >        green    </button>`;watch(state1, console.log);watch(stylestate, console.log);console.log(vdom, inputref);MountElement(vdom, document.getElementById("root"));

index.html

<script src="https://cdn.jsdelivr.net/gh/masx200/mvvm-reactive-view@latest/dist/polyfill.js"></script><div id="root"></div><script type="module" src="./index.js"></script>

支持jsx和使用 hyperscript

为什么选择jsx?而不是template

在体积方面,template编译器远大于jsx编译器。

浏览器中运行的JSX编译器HTM (Hyperscript Tagged Markup)体积小于 1KB

jsx的表现能力明显强于templatetemplate中无法写函数与对象,只能写字符串,

类似vueangular的模板DSL会让人很难理解,模板dsl不如jsx灵活

JSX非常流行,使用过react-jsx的人可以轻松使用,学习成本比较低

但是与react-jsx有一些不同,例如 属性名不使用驼峰命名等等

可在 rollupwebpack 中, 使用 babel-plugin-htm 或者 @babel/plugin-transform-react-jsx预编译成虚拟 dom

https://github.com/developit/htm

https://github.com/hyperhype/hyperscript

https://github.com/developit/htm/tree/master/packages/babel-plugin-htm

https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx

https://babeljs.io/docs/en/babel-plugin-transform-react-jsx

还需要使用babel-preset-env包含core-js@3"@babel/plugin-proposal-class-properties"

{    "plugins": [        [            "@babel/plugin-transform-react-jsx",            {                "pragma": "h",                "pragmaFrag": "\"\""            }        ],        [            "babel-plugin-htm",            {                "tag": "html",                "pragma": "h"            }        ],        "@babel/plugin-proposal-optional-catch-binding",        "@babel/plugin-proposal-nullish-coalescing-operator",        "@babel/plugin-proposal-class-properties"    ],    "presets": [        [            "@babel/preset-env",            {                "corejs": 3,                "useBuiltIns": "usage",                "targets": [                    "last 1 edge version",                    "last 1 safari version",                    "last 1 chrome version",                    "last 1 firefox version"                ]            }        ]    ]}

响应式状态对象 ReactiveState,可以独立于组件存在,可以在任何地方使用,

ReactiveState状态改变触发Event,触发函数也已经用 lodashdebounce函数包装成防抖函数,保证了短时间内只能触发一次事件

ReactiveState,基于 Proxy,

基于Proxy的深层数据劫持监听,对于数组Array和普通对象Plain Object理论上无限层次的数据观察代理

如果状态跟视图绑定,则状态改变引起界面刷新是异步的

可修改其value属性来改变状态的值,

初始值类型一旦确定,后续只能把相同类型的值赋给它

创建之后,对其value赋值,必须和初始类型相同

轻松使用全局共享状态,可以非常简单的集中统一管理,抛弃 redux,vuex,mobx

/** * @param {number} init * @returns{{    get: () => number;    increment: () => void;    decrement: () => void;}} */function create(init) {    /**     * @type{{value:number}}     */    const number = createState(init);    function increment() {        number.value++;    }    function decrement() {        number.value--;    }    const get = () => number.value;    const store = { get, increment, decrement };    return store;}const count = create(0);const mycomappclass = createComponent(() => {    const vdom = (        <div>            <h3> 点击数字</h3>            <h2>number:{count.get()}</h2>            <button onclick={count.increment}>increment</button>            <button onclick={count.decrement}>decrement</button>        </div>    );    return vdom;});const vdom = [    createElement(mycomappclass),    createElement(mycomappclass),    createElement(mycomappclass)];const container = document.createElement("div");MountElement(vdom, container);document.body.appendChild(container);

条件渲染

使用Condition函数来实现条件渲染,返回值是虚拟dom

var mystate = createState(true);var vdom = Condition(    mystate,    createElement("p", null, ["testtrue"]),    createElement("div", undefined, "testfalese"));setTimeout(() => {    mystate.value = false;}, 3000);const container = document.createElement("div");MountElement(vdom, container);document.body.appendChild(container);

组件化

使用createComponent 来创建组件,传参是一个组件初始化函数,返回一个web component custom element

在组件初始化函数里面可以使用useMounted,useUnMounted,watch,createState等函数

可以给组件设置默认属性,设置组件初始化函数的defaultProps属性即可

一个简单的helloworld示例如下

const defaultProps = { cccccc: "bbbbbbb" };const css = `* {  color: purple !important;  font-size: 50px;}`;const Hellowordclass = createComponent(    Object.assign(        () => {            return <div> hello world</div>;        },        { css, defaultProps }    ));const vdom = <Hellowordclass />;const container = document.createElement("div");MountElement(vdom, container);document.body.appendChild(container);

更新:可以不使用createComponent,创建组件了,内部会自动使用createComponent,自动转换

组件初始化函数需要返回一个虚拟DOM

最后给组件初始化函数包裹一个createComponent函数,返回一个web component custom element

组件局部 css 样式,设置组件初始化函数的css属性即可,可以使用 rollup-plugin-postcss或者to-string-loader引入外部 css 文件转成字符串

webpack to-string-loader

How to import css string from css file?

If you don't want to write CSS in JS, you can use to-string-loader of webpack, For example, the following configuration:

[    {        test: /[\\|\/]_[\S]*\.css$/,        use: ["to-string-loader", "css-loader"]    }];

If your CSS file starts with "_", CSS will use to-string-loader, such as:

const css = require("./_index.css");

rollup postcss plugin

import postcss from "rollup-plugin-postcss";postcss({    minimize: true,    extract: false,    inject: false});

样式隔离实现原理

在运行时,使用浏览器自带的css解析器,解析 css 文本变成cssrule,然后给selectorText添加前缀,再转换成 css 文本

转换前

div {    transform: rotate(30deg);}

转换后

foobar div {    transform: rotate(30deg);}

关于组件,元素的生命周期

每个组件,每个元素都有 创建, 挂载,更新,卸载 的生命周期

使用MutationObserver来高效的监听组件和元素的变化

给组件注册生命周期回调函数

使用useCreated来添加组件创建之后的回调函数

使用useMounted来添加组件挂载之后的回调函数

使用useUpdated来添加组件及其子节点更新之后的回调函数

使用useUnMounted来添加组件卸载之后的回调函数

使用useMounteduseUnMounted来给组件添加挂载和卸载时执行的callback函数,只能在组件初始化函数里面使用,这些callback函数都会异步执行

组件卸载时,组件内创建的响应式状态会自动取消watch,自动给元素删除事件监听器removeEventListener

组件挂载时,组件内创建的响应式状态会自动重新watch,自动给元素添加事件监听器addEventListener

父子组件传数据

组件之间的数据传递只能是从父组件到子组件的单向数据流,以json格式传递参数

抛弃mixin(混入)和hoc(高阶组件),让人难以理解,难以使用,它们已经被废弃。

https://blog.csdn.net/sinat_17775997/article/details/89181398

Mixin 带来的风险:

Mixin 可能会相互依赖,相互耦合,不利于代码维护

不同的 Mixin 中的方法可能会相互冲突

Mixin 非常多时,组件是可以感知到的,

甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性

HOCRender Props的缺陷

HOC 需要在原组件上进行包裹或者嵌套,如果大量使用 HOC,将会产生非常多的嵌套,这让调试变得非常困难。

HOC 可以劫持 props,在不遵守约定的情况下也可能造成冲突。

更好的逻辑组合复用方法

受到 Vue Composition APIReact Hooks的启发,

集各家所长,但是跟它们有很大不同,

响应式状态可以独立于组件存在,watch,computed,createState函数可以在组件外使用

基于函数的 API 提供与React Hooks相同级别的逻辑组合功能,但有一些重要的区别。

React hooks不同,该组件初始化函数仅被调用一次

使用useMounteduseUnMounted来给组件添加挂载和卸载时执行的函数,只能在组件初始化函数里面使用

使用watch函数来监听状态的变化,执行回调函数,可在任何地方使用此函数,传参 ReactiveState,或者 ReactiveState 数组,回调函数参数是unwrapped state的数组,返回一个取消观察 cancelwatch函数

函数watch的回调函数已经自动使用lodashdebounce方法包装成防抖函数了,确保短时间内回调函数只执行一次

var mystate = createState("aaeeqtt");const mycom = createComponent(    Object.assign(        (props, children) => {            useCreated(() => {                console.log("life-cycle-created");            });            useUpdated(() => {                console.log("life-cycle-updated");            });            useMounted(() => {                console.log("life-cycle-mounted1");            });            useUnMounted(() => {                console.log("life-cycle-unmounted");            });            watch(props.cccccc, console.log);            return createElement("div", null, [                "wwwwwwwwwwww",                createElement("div", null, ["createComponent"]),                children,                createElement
                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap