浅谈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 相关文章推荐
Add a Formatted Table to a Word Document
Jun 15 Javascript
jQuery学习笔记之总体架构
Jun 03 Javascript
JS获取当前日期时间并定时刷新示例
Mar 04 Javascript
input框中的name和id的区别
Nov 16 Javascript
javascript中闭包概念与用法深入理解
Dec 15 Javascript
详解基于node的前端项目编译时内存溢出问题
Aug 01 Javascript
pm2 部署 node的三种方法示例
Oct 20 Javascript
React实现全局组件的Toast轻提示效果
Sep 21 Javascript
js实现鼠标拖拽缩放div实例代码
Mar 25 Javascript
JavaScript提升机制Hoisting详解
Oct 23 Javascript
vue路由传参三种基本方式详解
Dec 09 Javascript
vue中对象数组去重的实现
Feb 06 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
配置最新的PHP加MYSQL服务器
2006/10/09 PHP
PHP学习笔记之二 php入门知识
2011/01/12 PHP
使用php语句将数据库*.sql文件导入数据库
2014/05/05 PHP
php的闭包(Closure)匿名函数详解
2015/02/22 PHP
分享五个PHP7性能优化提升技巧
2015/12/07 PHP
从零开始学习jQuery (六) jquery中的AJAX使用
2011/02/23 Javascript
javascript中的数字与字符串相加实例分析
2011/08/14 Javascript
js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
2012/05/16 Javascript
Jquery带搜索框的下拉菜单
2013/05/06 Javascript
分享一个自己动手写的jQuery分页插件
2014/08/28 Javascript
JavaScript提高加载和执行效率的方法
2017/02/03 Javascript
Vue非父子组件通信详解
2017/06/12 Javascript
Less 安装及基本用法
2018/05/05 Javascript
JavaScript中layim之整合右键菜单的示例代码
2021/02/06 Javascript
Python中字典和JSON互转操作实例
2015/01/19 Python
python使用wxpython开发简单记事本的方法
2015/05/20 Python
深入讲解Python函数中参数的使用及默认参数的陷阱
2016/03/13 Python
python3 实现验证码图片切割的方法
2018/12/07 Python
Python选择网卡发包及接收数据包
2019/04/04 Python
详解Python self 参数
2019/08/30 Python
在vscode中配置python环境过程解析
2019/09/28 Python
Python3实现二叉树的最大深度
2019/09/30 Python
Python用K-means聚类算法进行客户分群的实现
2020/08/23 Python
解决python便携版无法直接运行py文件的问题
2020/09/01 Python
python 服务器运行代码报错ModuleNotFoundError的解决办法
2020/09/16 Python
在Python中字典按值排序的实现方法
2020/11/12 Python
vue+django实现下载文件的示例
2021/03/24 Vue.js
班级文化建设标语
2014/06/23 职场文书
机动车交通事故协议书
2015/01/29 职场文书
工厂门卫岗位职责
2015/04/13 职场文书
新年晚会主持词开场白
2015/05/28 职场文书
文艺部部长竞选稿
2015/11/21 职场文书
全国劳模先进事迹材料(2016精选版)
2016/02/25 职场文书
入党申请书怎么写?
2019/06/11 职场文书
《家庭教育》读后感3篇
2019/12/18 职场文书
使用python如何删除同一文件夹下相似的图片
2021/05/07 Python