浅谈vue 单文件探索


Posted in Javascript onSeptember 05, 2018

在很多Vue项目中,我们使用 Vue.component 来定义全局组件,紧接着用new Vue({ el: '#container '}) 在每个页面内指定一个容器元素。

这种方案在只是使用 JavaScript 增强某个视图的中小型项目中表现得很好。然而在更复杂的项目中,或者当你的前端完全采用 JavaScript 驱动的时候,以下弊端就显现出来:

  • 全局定义(Global definitions) 强制要求每个 component 中的命名不得重复
  • 字符串模板(String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的\
  • 不支持CSS(No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤(No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 Webpack 或 Browserify 等构建工具。

以 vue 作为开发技术栈的前端开发者,往往会配合前端构建工具,进行项目的工程化管理。比如,大家常用的 vue 全家桶 + webpack 的方案进行一些中大型前端项目的开发。配合 webpack 后,vue 的组件化优势更加明显,我们可以通过单文件的组件化开发方式,在工作实践中搭建前端页面,从而提高开发效率。 有这样一个问题:“当我们在写 vue 单文件时,我们在写什么?” 很多人可能会这样回答:template 负责模板,javascript 负责逻辑,style 负责样式。当回答到这里时,一个 vue 开发者的世界观基本上算是很明确了。我们要做的就是在一个单文件组件中写 template、javascript、style。如果仅仅局限于此,显然我们无法从更好的利用的单文件组件服务我们的整个开发流程。接下来我将和大家讨论在 vue 单文件开发中的一些方法论的问题。

vue 单文件本质

vue单文件是以特定文件扩展名 .vue 命名的文件。如下所示的代码:

ListDemo.vue

<template>
  <div class="list-demo">
    <ul>
      <li v-for="item in list" :key="item.key">{{item.value}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'ListNav',
  data() {
    return {
      list: [
        { key: 'home', value: '首页' },
        { key: 'category', value: '文章分类' },
        { key: 'tags', value: '标签' },
        { key: 'about', value: '关于我' },
        { key: 'links', value: '友情链接'},
      ],
    };
  },
};
</script>
<style>
.list-demo {
  font-size: 14px;
}
</style>

代码中含有 template,script,style。三者的作用此处就不在赘述,如上的结构展示了一个 vue 单文件基本的文件结构。其背后的理念就是一个单文件组件对应了一个功能性组件,该组件的模板,样式,业务逻辑都采用就近维护的思想。从组件的复用性,后期可维护性的角度上来说,这样的理念都大大的提高了组件化的开发效率。vue 的单文件,既不是 js,也不是 html,也不是 css 文件,这样的文件如何被应用到页面上,这也就是下面将会说到的一个问题,vue 单文件是如何被处理成页面中可用的资源。

vue 单文件被处理的流程

vue 单文件配合 webpack 构建工具,在 webpack 中会交由 vue-loader 来处理。如下所示:

{
  test: /\.vue$/,
  loader: 'vue-loader',
}

项目中通过 import 或者 require 引入的 vue 单文件,都会经过 vue-loader 处理,vue-loader 在这个过程中会将模板按照 template、script、style 解析并将处理结果返回,三种不同类型的文件交由接下来的loader 进行处理。如果该单文件组件在父组件中的 components 声明,则 components 中对应的该项会被插入解析后 script 代码。这个过程从入口文件 main.js 开始,所有涉及的被依赖单文件组件依次经历这样的处理过程。之后所有的组件的实例化将根据业务逻辑中的依赖关系进行,这个过程也是我们平时在开发中经常用到的一种方式。(这里可以单拉一篇文章详细讲述 vue-loader 的处理流程)

单文件的常用姿势

模板中的组件引用

一、使用方式

组件的拆分和嵌套:

  • 将具体的业务按照功能以及后期复用性方面的考虑划分成更小的组件
  • 通过一个容器组件(父组件)将小的功能组件(子组件)进行整合

操作手法:父组件中引入子组件,components 中注册,template 中添加相应的组件引用模板

这种方式也是我们在进行单文件的开发中常用的一种方式,所有组件的实例化,都被隐含在组件的嵌套关系和业务逻辑中。开发者只需要关心组件的引入,在父组件逻辑中注册该组件,并在父组件的模板中以标签的方式引入组件。这个过程中待引入的组件的实例化时机也可以通过 v-if 指令在业务逻辑中进行控制。

二、适用场景

大部分场景下我们都可以通过这样的方式进行组件化的开发。这种模式的有一个特点: 组件的引入通过组件注册和模板中写入对应的组件的标签来完成。模板中通过标签来引入组件这一步必不可少,这个特点在某些业务场景下可能给开发者带来了一定的重复工作量。

API 式的调用

API 式的调用指的是手动创建子组件的实例,业务逻辑中无需引入组件和模板标签占位,在暴露的 API 中控制组件的实例化与显示。

一、使用方式

  • 功能模块提供一个入口 js 来控制该功能模块下单文件实例的所有功能逻辑
  • 其他组件中使用该功能模块时,调用功能模块下的 js,传入部分参数

操作手法:

Confirm.vue

<template>
  <el-dialg
    title="test"
    :visible.sync="visible">
    {{content}}
    <el-button @click="handleCancelClick">cancel</el-button>
    <el-button @click="handleOkClick">ok</el-button>
  </el-dialg>
</template>
<script>
export default {
  name: 'Confirm',
  data() {
    return {
      visible: false,
      content: '这是一个confirm dialog',
      callback: null,
    };
  },
  methods: {
    handleCancelClick() {
      this.callback('cancel');
    },
    handleOkClick() {
      this.callback('confirm');
    },
  },
};
</script>

confirm.js

import Vue from 'vue';
import Confirm from './confirm';
const ConfirmConstructor = Vue.extend(Confirm);
const confirm = (content) => {
  let confirmInstance = new ConfirmConstructor({
    data: {
      content,
    },
  });
  confirmInstance.vm = confirmInstance.$mount();
  confirmInstance.vm.visible = true;
  // 手动插入目的 dom
  document.body.appendChild(confirmInstance.vm.$el);
  confirmInstance.vm.callback = action => {
    return new Promise((resolve, reject) => {
     resolve(action);
    });
  };
  return confirmInstance.vm;
};

如上所示,给出的是一个确认弹框的场景实现。确认弹框在很多用户交互中是一个必须的交互形式。很多组件库也采用上面这种 API 式的组件调用。调用方仅仅通过 api 的调用,就能实现该功能模块的引用。这样就避免了在 template 中通过标签占位的方式引用。实现原理就是手动接管单文件组件的实例化,通过 Vue.extend 获得该组件对应的 Vue 的子类,在暴露给调用的 api 中去实例化这个组件。这个过程中我们可能还要完成一些组件数据的注入,逻辑相关以及手动将该组件插入到目的 dom 中。手动的注入 dom 是该种方式的一个很大特点,通过在 api 中动态的注入目的 dom,避免我们在各个业务组件中调用该功能模块时重复性的在业务组件 template 中手写组件标签。

二、适用场景

  • 功能聚合度高,组件内逻辑简单,输入输出较为单一,比如一些功能较为独立的弹框
  • 一些特殊的自定义指令开发,比如在一些特殊场景的指令,可以复用一些单文件组件,通过在指令的钩子中实例化组件对应的 vue 子类,按照特定的逻辑插入到目的 dom 中(例如:element-ui的v-loading)

区别和共性

共性:通过实例化对应组件完成组件的功能逻辑

区别:实例化的时机和方式不同。模板式的引入通过组件注册和标签引入的方式来使用单文件组件。标签引入解决了子组件插入的 dom 位置问题,开发者无需关心。API 式的单文件组件使用,在 API 调用时手动实例化组件,需要手动控制插入到目的 dom。

总结

vue 的单文件组件提供了 vue 的组件化开发思路,其本质在导出 vue 的一些关键属性,比如生命周期函数,methods,computed, watch,props等。我们可以通过上述两种方式来使用单文件组件,目的在于工程内部尽量减少重复的模板代码,组件解耦。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript中Math对象使用说明
Jan 16 Javascript
js页面滚动时层智能浮动定位实现(jQuery/MooTools)
Aug 23 Javascript
禁止选中文字兼容IE、Chrome、FF等
Sep 04 Javascript
JS 实现导航栏悬停效果(续)
Sep 24 Javascript
Javascript实现页面跳转的几种方式分享
Oct 26 Javascript
jquery获取当前日期的方法
Jan 14 Javascript
js ajaxfileupload.js上传报错的解决方法
May 05 Javascript
jQuery实现iframe父窗体和子窗体的相互调用
Jun 17 Javascript
JavaScript获取URL中参数querystring的方法详解
Oct 11 Javascript
微信小程序时间选择插件使用详解
Dec 28 Javascript
详解关于表格合并span-method方法的补充(表格数据由后台动态返回)
May 21 Javascript
用javascript制作qq注册动态页面
Apr 14 Javascript
快速解决vue动态绑定多个class的官方实例语法无效的问题
Sep 05 #Javascript
jQuery扩展方法实现Form表单与Json互相转换的实例代码
Sep 05 #jQuery
vue中动态添加class类名的方法
Sep 05 #Javascript
ng-events类似ionic中Events的angular全局事件
Sep 05 #Javascript
vue获取元素宽、高、距离左边距离,右,上距离等还有XY坐标轴的方法
Sep 05 #Javascript
vue 监听屏幕高度的实例
Sep 05 #Javascript
Vue-Router的使用方法
Sep 05 #Javascript
You might like
html静态页面调用php文件的方法
2014/11/13 PHP
IIS 7.5 asp Session超时时间设置方法
2017/04/17 PHP
最简单的jQuery程序 入门者学习
2009/07/09 Javascript
解析Javascript中大括号“{}”的多义性
2013/12/02 Javascript
浅析Node在构建超媒体API中的作用
2014/07/30 Javascript
node.js中的forEach()是同步还是异步呢
2015/01/29 Javascript
全面解析Bootstrap布局组件应用
2016/02/22 Javascript
jquery ztree异步搜索(搜叶子)实践
2016/02/25 Javascript
JavaScript简单实现弹出拖拽窗口(一)
2016/06/17 Javascript
EasyUI的doCellTip实现鼠标放到单元格上提示单元格内容
2016/08/24 Javascript
使用jQuery的load方法设计动态加载及解决被加载页面js失效问题
2017/03/01 Javascript
JavaScript实现向select下拉框中添加和删除元素的方法
2017/03/07 Javascript
JavaScript实现form表单的多文件上传
2020/03/27 Javascript
Vue 项目部署到服务器的问题解决方法
2017/12/05 Javascript
收集前端面试题之url、href、src
2018/03/22 Javascript
jQuery实现form表单序列化转换为json对象功能示例
2018/05/23 jQuery
vue-router的hooks用法详解
2020/06/08 Javascript
JS实现网站楼层导航效果代码实例
2020/06/16 Javascript
Python 中的 else详解
2016/04/23 Python
Python 常用的安装Module方式汇总
2017/05/06 Python
Python List cmp()知识点总结
2019/02/18 Python
Python进阶之使用selenium爬取淘宝商品信息功能示例
2019/09/16 Python
python 实现turtle画图并导出图片格式的文件
2019/12/07 Python
Win10用vscode打开anaconda环境中的python出错问题的解决
2020/05/25 Python
python实现梯度下降算法的实例详解
2020/08/17 Python
Smallable英国家庭概念店:设计师童装及家居装饰
2017/07/05 全球购物
新西兰最大、占有率最高的综合性药房:PharmacyDirect药房中文网
2020/11/03 全球购物
医学专业毕业生求职信
2014/06/20 职场文书
祖国在我心中演讲稿200字
2014/08/28 职场文书
2014年党建工作总结
2014/11/11 职场文书
2015年仓库工作总结
2015/04/09 职场文书
禁毒主题班会教案
2015/08/14 职场文书
2015年四年级班主任工作总结
2015/10/22 职场文书
2016春季运动会前导词
2015/11/25 职场文书
Nginx图片服务器配置之后图片访问404的问题解决
2022/03/21 Servers
Python 读取千万级数据自动写入 MySQL 数据库
2022/06/28 Python