首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Vue.js 2 基础用法

Vue.js 2 基础用法

作者头像
Cellinlab
发布2023-05-17 15:19:37
发布2023-05-17 15:19:37
7.5K00
代码可运行
举报
文章被收录于专栏:Cellinlab's BlogCellinlab's Blog
运行总次数:0
代码可运行

# Vue设计思想

  • 数据驱动应用
  • MVVM模式的践行者

# MVVM框架的三要素

  • 响应式 —— vue如何监听数据变化?
  • 模板引擎 —— vue的模板如何编写和解析?
  • 渲染 —— vue如何将模板转换为html?

# 模板语法是如何实现的

在底层的实现上,Vue将模板编译成虚拟DOM渲染函数。结合响应式系统,Vue能智能计算出最少需要重新渲染多少组件,并把DOM操作次数减到最少。

# 计算属性VS监听器

  • 优先使用computed
  • 语境上的差异
    • watch —— 一个值变化了我要做一些事情,适合一个值影响多个值的情形
    • computed —— 一个值由其他值得来,这些值变了我也要变,适合多个值影响一个值的情形
  • 计算属性具有缓存性,计算所得的值如果没有变化不会重复执行
  • 监听器选项提供了更通用的方法,适合执行异步操作或较大开销操作

# 生命周期

  • 使用场景分析
代码语言:javascript
代码运行次数:0
运行
复制
{
  beforeCreate () {}, // 执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务
  created () {}, // 组件初始化完毕,各种数据可以使用,常用于异步数据获取
  beforeMount () {}, // 未执行渲染、更新,dom未创建
  mounted () {}, // 初始化结束,dom已创建,可用于获取访问数据和dom元素
  beforeUpdate () {}, // 更新前,可用于获取更新前各种状态
  updated () {}, // 更新后,所有状态已是最新
  beforeDestroy () {}, // 销毁前,用于一些定时器或订阅取消
  destroyed () {}, // 组件已销毁,作用同上
}

# Vue 组件化的理解

组件化是Vue的精髓,Vue应用就是由一个个组件构成的

  • 定义:组件是可以复用的Vue实例,准确讲是VueComponent的实例,继承自Vue
  • 优点:组件化可以增加代码的复用性、可维护性和可测试性
  • 使用场景:
    • 通用组件:实现最基本的功能,具有通用性、复用性,如按钮组件、输入框组件、布局组件等
    • 业务组件:完成具体业务,具有一定的复用性,如登录组件、轮播图组件等
    • 页面组件:组织应用各个部分独立内容,需要时在不同页面组件间切换,如列表页、详情页组件
  • 如何使用组件: 定义:Vue.component(), components选项,sfc 分类:有状态组件,functional(不维护数据),abstract(不涉及视图) 通信:props,emit()/on(), provide/inject,children/parent/root/attrs/
  • 组件的本质:产生 虚拟 DOM
    • 过程:组件配置-> VueComponent 实例->render()-> Virtual DOM -> DOM

# 数据相关API

  • Vue.set(vm.$set)
    • 作用:向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新
    • 用法:Vue.set(target, property/index, value)
  • Vue.delete(vm.$delete)
    • 作用:删除对象的属性,如果对象是响应式的,确保能触发更新视图
    • 用法:Vue.delete(target, property/index)

# 事件相关API

# vm.$on

  • 作用:监听当前实例上的自定义事件,事件可以由 vm.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数
代码语言:javascript
代码运行次数:0
运行
复制
vm.$on('test', function(msg) {
  console.log(msg);
})

# vm.$emit

  • 作用:触发当前实例上的事件,附加参数都会传给监听器回调
代码语言:javascript
代码运行次数:0
运行
复制
vm.$emit('test', 'hello');

  • 典型应用:事件总线
    • 原理:通过在Vue原型上添加一个Vue实例作为事件总线,实现组件间相互通信,而且不受组件间关系的影响

# vm.$once

  • 作用:监听一个自定义事件,但是只触发一次,一旦触发后,监听器就会被移除
代码语言:javascript
代码运行次数:0
运行
复制
vm.$once('test', function(msg) {
  console.log(msg);
})

# vm.$off

  • 作用:移除自定义事件监听器
代码语言:javascript
代码运行次数:0
运行
复制
vm.$off(); // 没有提供参数时,移除所有的事件监听器
vm.$off('test'); // 如果只提供了事件,则移除该事件所有的监听器
vm.$off('test', callback); // 如果同时提供了事件与回调,则只移除这个回调的监听器

# 组件或元素引用

  • ref 和 vm.refs ref 被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 refs 对象上 如果在普通元素上使用,引用指向的就是 DOM 元素 如果用在子组件上,引用就指向组件 注意: ref 是作为渲染结果被创建的,在初始渲染时不能访问它们

# 过渡&动画

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果,包括:

  • 在 CSS 过渡和动画中自动应用 class
  • 可以配合使用第三方的 CSS 库,如 Animate.css
  • 在过渡钩子函数中使用 JS 直接操作 DOM
  • 可以配合使用第三方JS库,如 Velocity.js

CSS 过渡动画

  • 过渡类名
  1. v-enter:定义进入过渡的开始,在元素被插入之前生效,在元素被插入之后的下一帧失效
代码语言:javascript
代码运行次数:0
运行
复制
.fade-enter { opacity: 0; }

  1. v-enter-active: 定义进入过渡生效时的状态。在元素被插入之前生效,在过渡/动画完成之后移除
代码语言:javascript
代码运行次数:0
运行
复制
.fade-enter-active { transition: opacity 0.5s; }

  1. v-enter-to:定义进入过渡的结束状态。在元素被插入之后的下一帧生效(与此同时v-enter被移除),在过渡/动画完成之后移除
代码语言:javascript
代码运行次数:0
运行
复制
.fade-enter-to { opacity: 1; }

  1. v-leave:定义离开过渡的开始状态,在离开过渡被触发时立刻生效,下一帧被移除
代码语言:javascript
代码运行次数:0
运行
复制
.fade-leave { opacity: 1; }

  1. v-leave-active:定义离开过渡生效时的状态,在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。该类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
代码语言:javascript
代码运行次数:0
运行
复制
.fade-leave-active { transition: opacity 0.5s; }

  1. v-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效(与此同时v-else被删除),在过渡/动画完成之后移除
代码语言:javascript
代码运行次数:0
运行
复制
.fade-leave-to { opacity: 0; }

使用CSS动画库

通过自定义过渡类名可以有效结合 Animate.css 这类动画库制作动画效果

代码语言:javascript
代码运行次数:0
运行
复制
<transition
  enter-active-class="animated bounceIn"
  leave-active-class="animated bounceOut"></transition>

JS动画

可以在 <transition> 属性中声明 JS 钩子,使用 JS 实现动画

代码语言:javascript
代码运行次数:0
运行
复制
<transition  
  v-on:before-enter="beforeEnter" // 动画开始前,设置初始状态 
  v-on:enter="enter" // 执行动画
  v-on:after-enter="afterEnter" // 动画结束,清理工作
  v-on:enter-cancelled="enterCancelled" // 取消动画
  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"></transition>

列表过渡

利用 transition-group 可以对 v-for 渲染的每个元素应用过度

代码语言:javascript
代码运行次数:0
运行
复制
<transition-group name="fade">
  <div v-for="c in courses" :key="c.name">{{ c.name }} - ¥{{c.price}}
    <button @click="addToCart(c)">加购</button>
  </div>
</transition-group>

# 可复用性

# 过滤器

  • 用于一些常见的文本格式化
  • 可以用在两个地方:双花括号插值和 v-bind 表达式
代码语言:javascript
代码运行次数:0
运行
复制
<!-- 双花括号 -->
{{ message | capitalize }}
<!-- 在 v-bind 中 -->
<div v-bind:id="rawId | formatId"></div>

代码语言:javascript
代码运行次数:0
运行
复制
{{ c.price | currency('RMB) }}
filter: {
  currency(value, symbol = '¥') {
    return symbol + value;
  }
}

# 自定义指令

  • 需要对普通 DOM 元素进行底层操作,会用到自定义指令
代码语言:javascript
代码运行次数:0
运行
复制
Vue.directive('focus', {
  inserted(el) {
    el.focus();
  }
})

代码语言:javascript
代码运行次数:0
运行
复制
<input v-focus>

指令定义对象钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用,可在此进行一次性的初始化操作
  • inserted:被绑定元素插入到父节点时调用(仅保证父节点存在,但不一定已经被插入文档)
  • update:所在组件的 VNode 更新时调用,但可能发生在其子 VNode 更新之前
  • componentUpdate:指令所在组件的 VNode 及其子 VNode 全部更新后调用
  • unbind:只调用一次,指令与元素解绑时调用

在按钮权限控制中的应用

代码语言:javascript
代码运行次数:0
运行
复制
const role = 'user';
Vue.directive('permission', {
  inserted(el, binding) {
    if (role !== binding.value) {
      el.parentElement.removeChild(el);
    }
  }
})

代码语言:javascript
代码运行次数:0
运行
复制
<div class="toolbar" v-permission="'admin'"></div>

# 渲染函数

  • 在需要 JS 的完全编程的能力时使用,渲染函数比模板更接近编译器
代码语言:javascript
代码运行次数:0
运行
复制
render: function(createElement) {
  // createElement返回的结果是VNode
  return createElement(
    tag, // 标签名
    data, // 数据
    children, // 子节点数组
  )
}

示例

代码语言:javascript
代码运行次数:0
运行
复制
Vue.component('heading', {
  props: {
    level: {
      type: String,
      required: true,
    },
    title: {
      type: String,
      default: '',
    }
  },
  render(h) {
    return h(
      'h' + this.level, // tagname
      { attrs: { title: this.title } } // 参数
      this.$slots.default, // 子节点数组
    )
  }
})
// <heading :level="1" :title="title">{{ title }}</heading>

虚拟 DOM

  • Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM

createElement参数

代码语言:javascript
代码运行次数:0
运行
复制
// @return {VNode}
createElement(
  // {String | Object | Function} tagname
  'div', // 必填,标签名或组件名,也可以是返回前两个的函数

  // {Object}
  // 一个与模板中属性对应的数据对象,可选
  {
    //
  },

  // {String | Array}
  // 子级虚拟节点(VNodes),由createElement()构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”,可选
  [
    'some text',
    createElement('h1', 'another text'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar',
      }
    }),
  ]
);

# 函数式组件

  • 组件没有管理任何状态,也没有监听任何传递给他的状态,也没有生命周期,可以将组件标记为functional,即意味着它无状态(没有响应式数据),也没有实例(没有 this 上下文)
代码语言:javascript
代码运行次数:0
运行
复制
  Vue.component('heading', {
    functional: true,
    props: ['level','title','icon'],
    render(h, context) {
      let children = [];
      // 属性获取
      const { icon, title, level } = context.props;
      if (icon) {
        children.push(h(
          'svg',
          { class: 'icon' },
          [h('use', { attrs: { 'xlink:href': '#icon-' + icon } })],
        ));
        // 子元素获取
        children = children.concat(context.children);
      }
      vnode = h(
        'h' + level,
        { attrs: { title }},
        children,
      )
      return vnode;
    }
  })

# 混入

  • 提供一种非常灵活的方式来分发Vue组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被"混合"进入该组件本身的选项。
代码语言:javascript
代码运行次数:0
运行
复制
// 定义一个混入对象
var myMixin = {
  created: function() {
    this.hello();
  },
  methods: {
    hello: function() {
      console.log("hello mixin");
    }
  }
};
// 定义一个使用混入对象的组件
Vue.component('comp', {
  mixins: [myMixin]
});

# 插件

代码语言:javascript
代码运行次数:0
运行
复制
MyPlugin.install = function(Vue, options) {
  // 1. 添加全局方法或属性
  Vue.myGlobalMethod = function() {}

  // 2. 添加全局资源
  Vue.directive('my-directive', {})

  // 3. 注入组件选项
  Vue.mixin({
    created: function() {

    }
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function(methodOptions) {}
}

  • 组件改为插件方式
代码语言:javascript
代码运行次数:0
运行
复制
const MyPlugin = {
  install(Vue, options) {
    Vue.component('heading', {/*...*/});
  }
}
if (typeof window !== 'undefined' && window.Vue) {
  window.Vue.use(MyPlugin)
}

# 工程化

# Vue CLI

快速原型开发 安装全局扩展

代码语言:javascript
代码运行次数:0
运行
复制
npm i -g @vue/cli-service-global

启动一个服务并运行原型

代码语言:javascript
代码运行次数:0
运行
复制
vue serve Hello.vue

创建项目

代码语言:javascript
代码运行次数:0
运行
复制
vue create my-vue-app

插件

代码语言:javascript
代码运行次数:0
运行
复制
vue add router

开发

处理资源路径 在 JS、CSS 或 vue 文件中使用相对路径(必须以.开头)引用一个静态资源时,该资源将被 webpack 处理

转换规则

如果 URL 是一个绝对路径,会被保留

代码语言:javascript
代码运行次数:0
运行
复制
<img alt="vue logo" src="/assets/logo.png">
<img alt="vue logo" src="http://img.xx.com/logo.png">

如果 URL 以.开头会作为一个相对模块请求被解析并给予文件系统相对路径

代码语言:javascript
代码运行次数:0
运行
复制
<img alt="vue logo" src="./assets/logo.png">

如果 URL 以~开头会作为一个模块被请求被解析,即可以引用 Node 模块中的资源

代码语言:javascript
代码运行次数:0
运行
复制
<img alt="vue logo" src="~some-npm-pkg/foo.png">

如果 URL 以@开头会作为一个模块请求被解析,VueCLI 默认会设置一个指向 src 的别名 @

代码语言:javascript
代码运行次数:0
运行
复制
import Hello from '@/components/Hello.vue';

什么时候使用 public 文件夹 通过 webpack 的处理

  • 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求
  • 文件丢失会直接在编译时报错,而不是到了用户端才报错
  • 最终生成的文件名包含了内容哈希,因此不必担心浏览器缓存旧版本

什么时候直接用 public 文件夹

  • 需要在构建输出中指定一个固定的文件名字
  • 有上千图片,需要动态引用他们的路径
  • 有些库可能与 webpack 不兼容,除了将其用一个独立的 <script> 标签引入没有别的选择

使用 public 文件夹的注意事项

  • 如果应用不是部署在服务器的根目录,需要为 URL 配置 publicPath 前缀
代码语言:javascript
代码运行次数:0
运行
复制
// vue.config.js
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? '/cart/' : '/'
}

  • public/index.htmnl 等通过 html-webpack-plugin 用作模板的 HTML 文件中,需要设置链接前缀
代码语言:javascript
代码运行次数:0
运行
复制
<link rel="icon" href="<%= BASE_URL %>favicon.ico">

  • 在模板中使用先向组件传入 BASE_URL
代码语言:javascript
代码运行次数:0
运行
复制
data() {
  return {
    publicPath: process.env.BASE_URL,
  }
}
// 使用
// <img :src="`${publicPath}my-img.jpg"`">

CSS 相关

使用预处理器

代码语言:javascript
代码运行次数:0
运行
复制
# sass
npm i -D sass-loader node-sass

# Less
npm i -D less-loader less

# Stylus
npm i -D stylus-loader stylus

自动化导入样式 自动化导入样式文件(用于颜色、变量、mixin 等),可以使用 style-resources-loader

代码语言:javascript
代码运行次数:0
运行
复制
npm i -D style-resources-loader

配置

代码语言:javascript
代码运行次数:0
运行
复制
// vue.config.js
const path = required('path')
function addStyleResource(rule) {
  rule.use('style-resource')
    .loader('style-resources-loader')
    .options({
      patterns: [
        path.resolve(__dirname, './src/styles/import.scss'),
      ]
    })
}
module.exports = {
  chainWebpack: config => {
    const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
    types.forEach(type => {
      addStyleResource(config.module.rule('scss').oneOf(type))
    })
  }
}

Scoped CSS 当 <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素

代码语言:javascript
代码运行次数:0
运行
复制
<style scoped>
.red {
  color: red;
}
</style>

原理,使用 PostCSS 实现

代码语言:javascript
代码运行次数:0
运行
复制
<template>
  <div class="red" data-v-f3f3eg9>hello</div>
</template>
<style>
  .red[data-v-f3f3eg9] {
    color: red;
  }
</style>

混用本地和全局

代码语言:javascript
代码运行次数:0
运行
复制
<style>
  /* 全局样式 */
</style>
<style scoped>/* 本地样式 */</style>

深度作用选择器:使用 >>> 操作符可以使 scoped 样式中的一个选择器能够作用的更深

代码语言:javascript
代码运行次数:0
运行
复制
<style scoped>
#app >>> a {
  color: red;
}
</style>

Sass 等预处理器无法正确解析 >>>,这种情况下可以使用 /deep/::deep 操作符

代码语言:javascript
代码运行次数:0
运行
复制
<style scoped lang="scss">
#app {
  /deep/ a {
    color: red;
  }
  ::v-deep a {
    color: red;
  }
}
</style>

CSS Module CSSModules 是用于模块化和组合CSS的系统。vue-loader 提供了与 CSSModules 的集成,可以作为模拟 scoped CSS 的替代方案

代码语言:javascript
代码运行次数:0
运行
复制
<style module lang="scss">
.red {
  color: red;
}
.bold {
  font-weight: bold;
}
</style>

模板中通过 $style.xxx 访问

代码语言:javascript
代码运行次数:0
运行
复制
<a :class="$style.red">awesome-vue</a>
<a :class="{[$style.red]: isRed}">awesome-vue</a>
<a :class="[$style.red, $style.bold]">awesome-vue</a>

在 JS 中访问

代码语言:javascript
代码运行次数:0
运行
复制
<script>
export default {
  created() {
    console.log(this.$style.red); // red_1vyoJ-uz 一个基于文件名和类名生成的标识符
  }
}
</script>

# 数据访问相关

数据模拟 使用开发服务器配置 before 选项,可以编写接口,提供模拟数据

代码语言:javascript
代码运行次数:0
运行
复制
// vue.config.js
devServer: {
  before(app) {
    // app是一个express
    app.get('/api/courses', (req, res) => {
      res.json([
        { name: 'web', price: 10 },
        { name: 'api', price: 19 }
      ])
    })
  }
}

使用

代码语言:javascript
代码运行次数:0
运行
复制
// api/courses.js
import axios from 'axios';

export function getCourses() {
  return axios.get('/api/courses').then((res) => res.data);
}

请求代理 设置开发服务器代理选项代理可以有效避免调用接口时出现的跨域问题

代码语言:javascript
代码运行次数:0
运行
复制
// vue.config.js
devServer: {
  proxy: 'http://localhost:3000'
}

# SSR

服务端渲染:将 vue 实例渲染为 HTML 字符串直接返回,在前端激活为交互程序

  • 优点
    • seo
    • 首屏内容到达时间
  • 缺点
    • 开发逻辑复杂
    • 开发条件限制,如一些生命周期不能使用,一些第三方库会不能用
    • 服务器负载大
  • 已存在 spa 转 SSR
    • 需要 SEO 的页面是少数可以考虑预渲染
    • 利用 puppeteer 虚拟运行后在返回(即在服务端模拟浏览器渲染出结果再返回给用户端)
    • 重构
    • nuxt.js 重写
  • 基础实现 使用渲染器将 vue 实例变成 html 字符串并返回
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/1/16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • # Vue设计思想
  • # MVVM框架的三要素
  • # 模板语法是如何实现的
  • # 计算属性VS监听器
  • # 生命周期
  • # Vue 组件化的理解
  • # 数据相关API
  • # 事件相关API
    • # vm.$on
    • # vm.$emit
    • # vm.$once
    • # vm.$off
  • # 组件或元素引用
  • # 过渡&动画
  • # 可复用性
    • # 过滤器
    • # 自定义指令
    • # 渲染函数
    • # 函数式组件
    • # 混入
    • # 插件
  • # 工程化
    • # Vue CLI
    • # 数据访问相关
  • # SSR
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档