为什么我们还要使用前端框架来构建页面?
发布于 5 年前 作者 zy445566 7700 次浏览 来自 分享

今天一个同事说现在前端一定要使用前端框架,现在复杂应用不用就写不下去。我说我们很多场景下未必就一定需要前端框架,很多时候没有框架反而会更好。并且我相信随着Web规范的一步一步完善,去框架化的时代或许不久就会到来。

我列举了几个使用前端框架的坏处:

  • 给每个页面无端带上了多余的上百K的请求负担
  • 过于依赖框架,让我们成为框架工程师
  • 对理解底层JS实现增加障碍
  • 框架对代码进行编译和封装,让我们在棘手问题的调试更加困难

同时列举无框架化该如何实现组件的封装,路由跳转,双向数据绑定,列表渲染!

注意:本文基于chrome66以上版本!!!

本文例子代码地址:点此打开本文example代码

构造一个具有封装性的组件

首先我们需要增加一个index.html,里面添加一个自定义标签app-container,作为我们的app容器,同时引用一个模块化的js文件用type="module"来区分,如下:

<!DOCTYPE HTML>
<html>
    <head>
        <title>show time</title>
    </head>
<body>
    <app-container />
    <script type="module" src="/app.js"></script>
</body>
</html>

接下来我们来完成app.js,首先我们需要创建一个模版,其中包含了自定义路由标签,但不需要管,后面会说到路由的实现。

const template = document.createElement('template');
        template.innerHTML = `
        <style>
            .container > h1 {
                width:120px;
                border: solid;
                cursor: pointer;
            }
        </style>
        <div class="container">
            <h1>Click Me!</h1>
            <my-router>
                <my-browse-route path="/data-bind" tag="data-bind"></my-browse-route>
                <my-browse-route path="/add-list" tag="add-list"></my-browse-route>
            </my-router>
        </div>`;

然后把创建的模版来生成一个关闭的封装模式的web组件,同时使用customElements.define来注册自定义的web组件,到这里app-container标签就成功生成为一个web组件完成封装。 如下是app.js的代码:

// 省略一些代码
class AppContainer extends HTMLElement {
    constructor() {
        super();
        const template = document.createElement('template');
        template.innerHTML = `...模版代码,这里省略`;//就是上面的代码,省略
        const content = template.content.cloneNode(true);
        // 这里是设定封装模式,可以设置是否能受到外界因素影响
        const shadow = this.attachShadow( { mode: 'closed' } );
        // 省略一些代码...
        shadow.appendChild(content);
    }
  }
window.customElements.define('app-container', AppContainer);

一个路由跳转组件的实现

如app.js所示的路由标签如下所示,那么我们需要做两个组件分别是my-router和my-browse-route。

<my-router>
    <my-browse-route path="/data-bind" tag="data-bind"></my-browse-route>
    <my-browse-route path="/add-list" tag="add-list"></my-browse-route>
</my-router>

那么我们第一个路由组件my-router只需要一个空壳来包装路由文件的内容,而my-browse-route则需要根据目前的路由来显示内容。

那么一个空壳组件my-router其实只需要,如下实现:

export default class MyRouter extends HTMLElement {
    constructor() {
        super();
    }
}

当然组件my-browse-route也只需要寥寥代码即可实现:

export default class MyBrowseRoute extends HTMLElement {
    constructor() {
        super();
        const template = document.createElement('template');
        // 获取path属性来决定当前路由是否渲染内容
        this.path = this.getAttribute('path');
        // 获取tag属性来决定显示那个组件
        this.tag = this.getAttribute('tag');
        template.innerHTML = this.getHtml()
        const content = template.content.cloneNode(true);
        const shadow = this.attachShadow( { mode: 'closed' } );
        shadow.appendChild(content);
    }
    getHtml() {
        // 这里是如果当前路由不等于设置路由则直接返回空html来渲染
        if(window.location.pathname!=this.path) {return ''};
        return `<${this.tag}/>`
    }
  }

实现双向绑定的例子

其实双向绑定实现的方式有很多,我使用了一个代码量较少的方式来实现,即一开始就劫持数据,并在数据变化的时候,重新渲染模版如下:

export default class DataBind extends HTMLElement {
    constructor() {
        this.data = this.dataBind({
            inputVal:'hello'
        }); 
        // 省略若干代码...
    }
    dataBind(data) {
        // 这里进行数据劫持
        return new Proxy(data, {
            set:  (target, key, receiver) => {
                Reflect.set(target, key, receiver)
                // 这里进行重新渲染模版
                this.shadow.innerHTML = this.getHtml();
                return Reflect.set(target, key, receiver);
        }})
    }
    getHtml() {
        // 这里获取数据的最新模版
        return `
        <di>
            <input type="text" value="${this.data.inputVal}"/>
            <h4>${this.data.inputVal}</h4>
        </div>`;
    }
  }

列表渲染的实现

列表渲染更是简单,只需要利用一下模版字符串的一些小技巧既可以实现列表渲染,如下。

export default class AddList extends HTMLElement {
    constructor() {
        // 这里设置默认值
        this.data = {
            inputVal:'hello',
            list:[]
        }
        //  省略一些代码... 
        const content = template.content.cloneNode(true);
        const myUl = content.querySelector('ul');
        // 这里根据按钮点击重新渲染列表
        myBtn.addEventListener('click',()=>{
            if(myInput.value) {
                this.data.list.push(myInput.value);
                myUl.innerHTML = this.getLi()
            }
        })
        //  省略一些代码... 
    }
    // 这里就是遍历list数据来重新渲染列表模版
    getLi() {
        return `${this.data.list.map((val)=>{
                return `<li>${val}</li>`
            }).join('')}`
    }
  }

如果想要集成数据绑定和模版渲染写个基类,然后上层都继承基类即可。

总结

不使用前端框架而使用web组件来替代的好处就是简单的页面基本都是几十B(是B不是KB)就搞定,在渲染的时候自己可控性也强导致性能可以优化的更猛,更原生的体验,不需要热加载,刷新即可见。debugger都是原生代码而不是框架处理后的代码,摆脱框架束缚。自己搭框架也不过是百行,同时随着方法即服务流行扩大,这种无框架架构将会受到更大的支持。

注意:本文基于chrome66以上版本!!!

本文例子代码地址:点此打开本文example代码

14 回复

好难找到一个拥有这种觉悟的队友或者团队

vue、react、angular 这些的价值在于社区,标准化带来的学习成本、团队沟通成本、经验沉淀成本降低。除非你觉得世界上那么多用 vue、react、angular 的人都是傻子。

自己随便玩玩可以,在业务中就要慎重了,再简单的场景,再简单的代码,除非你只有一个应用,否则你最后还是会封装为自己的一个『框架』,然后随着业务的演进必然会集成越来越多的功能,不可避免的会走向勇士成为恶龙的道路,哪天你离职后就是给后人埋雷。

前端的深水区那么多,有那么多可以深挖的地方(CloudIDE、可视化、富文本编辑器、AR/VR。。。),就别在已经基本标准化完成的前端框架这一领域浪费时间了,人生苦短,要找到有真正价值(不仅仅对自己,还有团队和社区)的地方去奋斗。

@atian25 其实取标题的时候,我也犹豫过要不要叫“web components快速实现框架功能”,想想不够震惊。说白了,即使在小团队内使用也一定会存在功能的封装,随着封装加剧,也会像框架一样。

评论很犀利,很多地方貌似直击要害,但可能又不算要害。

标准确实能带来很多好处,但是如果抛开我上面实现的功能来说。vue、react、angular 属于一种公司或私立的框架标准。而web components属于浏览器标准,为什么要选择框架标准而放弃浏览器标准?

其实在业务中,不管是vue、react、angular,公司内部往往会在它之上添加很多自己公司特色的功能,可以说是框架的框架。而使用web components其实也一样,不管如何都会在公司内部再封装一层,所以恶龙是不可避免的,埋雷总是存在的,不管是用什么框架或标准。

其实现在觉得抵制的人,很多就是和当年Jquery的人的心态一致,就是我已经用它构建了这么多有意义的东西了,为什么还要用我不熟悉的vue、react、angular?然而我觉得学一样新东西未必就是坏事。

自己能根据公司的业务封装框架,和直接用现成的框架对码农来说,哪种选择更有益?

@HobaiRiku 老实说我觉得目前这个东西很适合给团队内部或基建工作引入,因为这个东西目前所带来的价值之一就是提升团队的技术面同时可以先沉淀,先平筛选出优缺点。如果以后社区支持度更高的时候,也可以优先把这方面沉淀公布不仅仅能给社区带来贡献,也能给团队带来价值。即使以后不会流行就当会点新东西,感觉也亏不到哪去。

你的想法和我如出一辙,首先用一个真实数据做对比,我们的pc官网和wap网站的性能对比差了10倍,打开速度pc网站0.3s,wap网站3s。因为pc做的比较早,使用了最基础的jquery开发,而wap网站为了追求新技术使用了vue,使用框架的结果是增加了开发难度,编译变慢,引入了体积较大了js。框架对于开发复杂项目会降低开发难度,但是对于简单网站却是增加了难度。

个人观点:使用什么技术是技术人的向上追求,当别人都很原始的时候我使用新技术,新框架就比别人强,当所有人都使用框架的时候我回归自然,使用最简单原始的开发方式就会显得我更懂底层原理。这是一个周期性的波动特性。

@zy445566 web components 成为标准的那一天,vue、react、angular 它们也肯定已经做了平滑的支持。它们的内部完全可以重构为 web components,而对于上层开发者是无感知的。就像 Vue 2 -> 3 把 defineProperty 改为 Proxy 绝大部分情况是对上层开发者无感知的(当然因为有其他的优化和对浏览器有要求所以发了大版本)。

而且 web components 的标准化过程中,必然会考虑到 vue、react、angular 的,所有的标准定制都是从事实标准中演化过来的。如果 web components 的标准化是完全抛开三大框架,那它必然会是另一个 ES4。

我并没有表达我抵制 web components,我表达的是作为一个团队的 Leader,从我的角度我会更倾向于找到一种平衡的演进路线,更何况三大框架只有 web components 这一点么?我会判断我团队的价值应该落到哪里,我并不认为我的团队做的东西会超过三大框架,我更宁愿参与进去三大框架来演进到那个未来。

屁股决定脑袋,不同的团队,不同的阶段,看的的价值都是不一样的,我也不是在犀利的攻击你的观点,我只是表达从我的角度我会做的选择。

end of discussion~ 我都已经淡出前端框架这个领域了~ 当我没回复吧。

@yuedun 其实很多人就是发现了这个问题,所以现在有人也在研究按需加载的轻量框架。

对于笔者的观点我很支持,vue和react 个人更加偏向于使用业务复杂的,对于简单的内容类网站确实不是很适用 现在大部分初级前端vue和react 都会用,但是只是会用,面试的时候一问,原理都不知道,都是按照网上的说的,继续问,什么也不会,原生js,基本上都不会, 最近在思考原生js,不是做前端的基本功吗? 对框架使用很熟练,就好像一直在做一件重复的是,很难跳出舒适圈,个人更加认为应该学习原理!而不应该局限在熟练使用框架,

无聊,这种问题三天两头蹦出来没啥意义。

想到一点 如果是社交应用(前端时间一个jq老项目被搞) 还得考虑xss 但是三大框架帮你考虑了 你这加一下应该也不难 不过成本高啊 得考虑团队每一个人是不是都有安全意识 是不是都能把这个问题处理好 就像说用hooks实现状态管理多么轻量 结果呢 真的那么简单就撸出来一个状态管理到生产里面用么

用框架也不耽误学习底层的js知识,只能说有些人自己把自己搞成框架工程师

回到顶部