vue2 语法快速入门

前言

vue2时代即将结束,vue3是不可阻挡的,学习新技术永不终止 !!!

对vue2语法有所了解vue3直接可以开发

 vue2是双向数据绑定,当数据发生改变影响视图,视图操作也会影响数据变化,是相互的。

1、vue2创建 

 vue create my-project

选择vue2进行安装,根据提示启动项目

package.json   

了解下相关属性配置

  scripts - 启动模式
  dependencies  --项目依赖包
  devDependencies --本地开发依赖包
  eslintConfig --eslint配置
  browserslist --浏览器支持
 
2、模板语法花括号{{}}
   
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

虚拟DOM-渲染函数

  双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令

 

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

 

 XSS(跨站脚本攻击) v-html防范录入不可信内容

2、v- 指令(Directives)是带有 v- 前缀的特殊属性:
  • v-bind: 绑定属性  简化:  :bind
  • v-if: 条件渲染  v-if与v-show区别 v-if DOM销毁,v-show使用样式隐藏,页面获取不到DOM相关信息
  • v-for: 列表渲染  v-for 考虑虚拟DOM渲染算法diff, key不可以缺少
  • v-on: 事件监听  简化  @click
  比如vue中的v-if 和 v-show指令,在使用一些隐藏情况的时候,我们也要去考虑重绘重排的问题,提高性能。
 
3计算属性
  模板内的表达式非常便利,设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护
  
<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

computed计算属性凸出计算汇总,比如总价、多个运算结果。简单理解展示====>【最终结果】

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

 

计算属性 vs 方法

  方法调用

method-->{{reversedMessage()}}  无缓存,每次都会重新计算

计算调用
如果监听属性值无没有改变,会在缓存直接取值; 有变化会重新计算, 提升运算效率

计算属性 vs 方法

  computed: 有缓存,复杂结果计算
  watch:无缓存,对某个属性监听
 
watch: {
    name1: {
      handler(newval) {
        console.log(newval)
      },
      immediate: true, // 加载就会监听
      deep:true  // 深度监听
    }
  },

 

Class 与 Style 绑定

<div v-bind:class="{ active: isActive }"></div>

 

isActive:true 会展示class= 'active'  or   isActive==true

  当为true 显示该类名;

数组更新检测

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()  [splaɪs]
  • sort()
  • reverse()

 

image

 

===========splice 使用===========

 const arr = ['a', 'b', 'c', 'd']

 

1、根据 :索引删除元素 两个参数时(索引,标识1-删除)

 arr.splice(0,1)  // ['b', 'c', 'd']

2、根据:指定索引添加元素 三个参数时 (索引,标识0-追加,值)

arr.splice(0, 0, '222') //['222', 'a', 'b', 'c', 'd']

3、根据:指定索引替换元素 三个参数(索引,标识1-替换,值)

arr.splice(0, 1, '222') //['222', 'b', 'c', 'd']

 

>>>不会变更原始数组,而总是返回一个新数组

map()filter()slice()concat()includes()

 

  • 会改变原数组:push()pop()unshift()shift()splice()sort()reverse()
  • 不改变原数组:map()filter()slice()concat()includes()

 

1、 forEach():无返回值,纯遍历

arr.forEach((item, index, array) => {
  console.log(item, index);
});

2、map():返回新数组(每个元素经处理后的值)

const doubled = [1, 2, 3].map(item => item * 2); // [2,4,6]

 

3、filter():返回符合条件的元素组成的新数组

const evens = [1, 2, 3, 4].filter(item => item % 2 === 0); // [2,4]

4、find() / findIndex():返回第一个符合条件的元素 / 索引

5、every() / some():判断是否全部 / 部分元素符合条件

6、reduce():累加器,将数组缩减为单个值

const sum = [1, 2, 3].reduce((acc, item) => acc + item, 0); // 6

 

7、slice(start, end):截取子数组(不包含 end),不改变原数组

[1,2,3,4].slice(1, 3); // [2,3]
[1,2,3].slice(1); // [2,3](从索引1到末尾)

 

在 JavaScript 中,substr()substring() 和 slice() 都是用于截取字符串(或数组,仅 slice() 支持数组)的方法,

但它们的语法和行为存在差异,容易混淆

一、字符串方法:substr()(已不推荐使用) 

语法:str.substr(startIndex [, length])

const str = "abcdefg";
str.substr(2, 3);    // "cde"(从索引2开始,取3个字符)
str.substr(-3, 2);   // "ef"(从末尾第3个字符开始,取2个)
str.substr(4);       // "efg"(从索引4开始取到末尾)

 

二、字符串方法:substring()

语法:str.substring(startIndex [, endIndex])

const str = "abcdefg";
str.substring(2, 5);  // "cde"(从索引2到4,不包含5)
str.substring(5, 2);  // "cde"(自动交换,等价于 substring(2,5))
str.substring(-1, 3); // "abc"(start为负数,视为0)
str.substring(3);     // "defg"(从索引3取到末尾)

三、字符串 / 数组方法:slice()(推荐vvvvvv)

  • 字符串:str.slice(startIndex [, endIndex])
  • 数组:arr.slice(startIndex [, endIndex])
const str = "abcdefg";
str.slice(2, 5);    // "cde"(从索引2到4,不包含5)
str.slice(-4, -1);  // "def"(从末尾第4个到末尾第1个,不包含末尾)
str.slice(5, 2);    // ""(start > end,返回空)
str.slice(3);       // "defg"(从索引3取到末尾)

  示例(数组):

 

const arr = [10, 20, 30, 40, 50];
arr.slice(1, 4);    // [20, 30, 40](从索引1到3)
arr.slice(-3);      // [30, 40, 50](从末尾第3个取到末尾)
arr.slice(2, -1);   // [30, 40](从索引2到末尾第1个,不包含末尾)

 

  • 优先使用 slice():支持负数索引、适用于数组和字符串,行为更可预测。
  • 避免使用 substr():属于非标准方法,未来可能被移除。
  • 若需兼容旧代码使用 substring(),注意其对负数和 start > end 的特殊处理。

 

v-for 与 v-if 一同使用

注意我们不推荐在同一元素上使用 v-if 和 v-for
当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,
 
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

上面的代码将只渲染未完成的 todo。

而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 <template>) 上。如:

 

<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>

事件处理方法

 
<button @click="greet(xxx,$event)">Greet</button>

 

Prop

props: {
  // 必传,且必须是字符串
  title: {
    type: String | String,
    required: true
  },
  
  // 数字类型,默认值为 100
  count: {
    type: Number,
    default: 100
  },
  
  // 数组/对象的默认值必须通过函数返回
  list: {
    type: Array,
    default: () => [] // 避免多个组件实例共享同一个引用
  },
  
  config: {
    type: Object,
    default: () => ({ size: 'medium', color: 'blue' })
  }
}

prop 是单向数据流,子组件不应直接修改父组件传递的 prop,如需修改,应通过 $emit 通知父组件更新。

props: {
  // 数组:确保每个元素都是字符串
  tags: {
    type: Array,
    validator: (value) => {
      return value.every(item => typeof item === 'string');
    }
  },
  
  // 对象:确保包含指定属性
  user: {
    type: Object,
    validator: (value) => {
      return 'id' in value && 'name' in value;
    }
  }
}

.sync 修饰符

当父组件向子组件传递 prop,且子组件需要修改这个 prop 时,常规做法是:

 

    1. 父组件通过 prop 传值给子组件
    2. 子组件通过 $emit 触发事件,通知父组件更新值

sync 修饰符可以简化这个过程,省去手动编写事件监听的代码。

常规做法:父组件

<template>
  <div class="hello">
    ----isVisible:{{ isVisible }}
    <page-one :isVisible="isVisible" @visibleFn='visibleFn'></page-one> </div> </template>

// 方法
visibleFn(){
this.isVisible = false

}

 

sync简化后:

父组件

<template>
  <div class="hello">
    ----isVisible:{{ isVisible }}
    <page-one :isVisible.sync="isVisible"></page-one>
  </div>
</template>

子组件

   updateFn() {
      this.$emit('update:isVisible', false)
    }

事件命名规范
子组件必须触发 update:xxx 格式的事件(xxx 对应 prop 名称),才能配合 sync 生效

 
》对象类型的 sync
可以对对象的所有属性使用 sync,简化多个属性的双向绑定:
<!-- 父组件 -->
<template>
  <Child v-bind.sync="user" />
</template>
 user: { name: '张三', age: 20 }
<!-- 子组件 -->
 this.$emit('update:user', {...this.user,name:'新同学'});

 

 

插槽内容

1. 子组件中定义插槽(使用 <slot> 标签)

<!-- 子组件 Child.vue -->
<template>
  <div class="card">
    <!-- 插槽:父组件的内容会插入到这里 -->
    <slot></slot>
  </div>
<!-- 父组件 Parent.vue -->
<template>
  <Child>
    <!-- 这里的内容会被插入到子组件的 <slot> 位置 -->
    <h3>这是卡片标题</h3>
    <p>这是卡片内容...</p>
  </Child>
</template>

 

2、具名插槽(Named Slots)

当子组件需要多个插槽时,可以通过 name 属性给插槽命名,父组件按名称插入对应内容:
 
<!-- 子组件 List.vue -->
<template>
  <ul>
    <li v-for="(item, index) in items" :key="index">
      <!-- 通过 v-bind 向插槽传递数据(item 和 index) -->
      <slot :item="item" :index="index" :otherData=" '额外信息' "></slot>
    </li>
  </ul>
</template>

<script>
export default {
  props: {
    items: {
      type: Array,
      default: () => []
    }
  }
};
</script>
<!-- 父组件 Parent.vue -->
<template>
  <div>
    <List :items="fruits">
      <!-- 通过 slot-scope 接收子组件传递的所有数据 -->
      <template slot-scope="scope">
        <p>索引:{{ scope.index }}</p>
        <p>内容:{{ scope.item }}</p>
        <p>附加:{{ scope.otherData }}</p>
      </template>
    </List>
  </div>
</template>

<script>
import List from './List.vue';

export default {
  components: { List },
  data() {
    return {
      fruits: ['苹果', '香蕉', '橙子']
    };
  }
};
</script>
</template>

 

3、作用域插槽(Scoped Slots)

子组件可以向插槽传递数据(作用域),让父组件在插入内容时能使用子组件的数据
<template>
  <div class="child-component">
    <slot name="item" v-for="item in items" :item="item" :key="item.id">
      <!-- 默认的 item 内容 -->
      <p>{{ item.text }}</p>
    </slot>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      items: [
        { id: 1, text: 'Item 1' },
        { id: 2, text: 'Item 2' }
      ]
    }
  }
}
</script>

父组件

<!-- 父组件 Parent.vue -->
<template>
  <div>
    <List :items="fruits">
      <!-- 通过 slot-scope 接收子组件传递的所有数据 -->
      <template slot-scope="scope">
        <p>索引:{{ scope.index }}</p>
        <p>内容:{{ scope.item }}</p>
        <p>附加:{{ scope.otherData }}</p>
      </template>
    </List>
  </div>
</template>

<script>
import List from './List.vue';

export default {
  components: { List },
  data() {
    return {
      fruits: ['苹果', '香蕉', '橙子']
    };
  }
};
</script>

 

 

动态组件 & 异步组件

1、动态组件切换

在当前主组件页面,引入子组件 PageOne.vue 和 PageTwo.vue页面

 <div class="hello">
    <button @click="curComponent = 'PageOne'">组件1</button>
    <button @click="curComponent = 'PageTwo'">组件2</button>
    动态组件:<br />
    <component :is="curComponent"></component>
  </div>

属性值:

curComponent 改变 组件会重新创建及加载,每次都会重新渲染

 当操作频繁保留当前状态值或滚动条位置 配合  keep-alive 缓存组件状态
    <keep-alive>
      <component :is="curComponent"></component>
    </keep-alive>

 

缓存指定组件

通过 include 或 exclude 属性控制缓存范围(值为组件名,多个用逗号分隔)

!!!组件名====>子组件中name属性值

 

<!-- 只缓存 ComponentA -->
<keep-alive include="ComponentA">
  <component :is="currentComponent"></component>
</keep-alive>

<!-- 不缓存 ComponentB -->
<keep-alive exclude="ComponentB">
  <component :is="currentComponent"></component>
</keep-alive>

 

动态组件传参

和普通组件一样,动态组件可以通过 props 传递数据,通过 v-on 监听事件:
  <component 
    :is="currentComponent" 
    :title="title" 
    @change="handleChange"
  ></component>
  • 子组件(如 ComponentA)通过 props 接收 title,通过 $emit('change', 值) 触发事件。

 

使用组件选项对象-异步加载组件

通过 import() 异步加载组件,然后将加载后的组件赋值给 is 绑定的变量,实现动态渲染

<template>
  <div>
    <!-- 动态组件挂载点 -->
    <component :is="currentComponent"></component>
    
    <button @click="loadComponent('ComponentA')">加载组件A</button>
    <button @click="loadComponent('ComponentB')">加载组件B</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentComponent: null // 初始为空,不渲染任何组件
    };
  },
  methods: {
    // 动态导入并加载组件
    async loadComponent(name) {
      try {
        // 根据组件名异步导入对应的组件文件
        const module = await import(`./components/${name}.vue`);
        // 将组件赋值给 currentComponent,动态组件会自动渲染
        this.currentComponent = module.default;
      } catch (error) {
        console.error('组件加载失败:', error);
      }
    }
  }
};
</script>
  • 原理:import('./components/ComponentA.vue') 会返回一个 Promise, resolve 后得到的 module.default 就是组件的选项对象,可直接赋值给 :is
  • 注意:import() 的路径需是静态可分析的(如 ./components/${name}.vue 中 name 是动态变量,但基础路径固定),避免完全动态的路径(如 import(path) 可能导致打包失败)。
  1. 类型判断:import() 加载的组件是选项对象,赋值给 :is 时无需额外处理(Vue 会自动识别)。
 
 
 
 

posted on 2025-08-18 18:28  Mc525  阅读(32)  评论(0)    收藏  举报

导航