Vue使用记录,持续更新……

记录于  2023-04-20

修改于  2024-02-17


Vue的语法糖好多,由于公司项目版本2和3互相掺杂,有些写法和技巧在不同版本间互有差异,我很容易将二者的写法混淆,故记录一些常见的问题的解决方案,持续更新……


1.生命周期

preview

2.规则

2.1组件

2.2选项式组件顺序

推荐按如下顺序编写单文件选项式组件代码,

<template>
  <div></div>
</template>
<script>
export default {
  name: 'vue',
  components: {},
  props: {},
  emits: [],
  expose: [],
  data() {
    return {}
  },
  computed: {},
  watch: {},
  created(){},
  beforeMount(){},
  mounted() {},
  beforeUnmount(){},
  unmounted() {},
  methods: {},
}
</script>

<style scoped lang="scss">

</style>

原因有:

3.通信

3.1父子组件变量绑定1

1.借用自定义事件。

子组件绑定表单输入数据并自定义事件,当输入变化时释放值,由父组件绑定事件回调,接收并修改自身数据。

将父组件data注入到子组件props里,后者再赋值data,实现值初始化

// 子组件
<template>
  <div class="hello">
    <label for="子组件">子组件</label>
    <input type="text" v-model="value" @keyup="send" />
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  emits: ["value"],
  props: {
    msg: String,
  },
  data() {
    return {
      value: "",
    };
  },
  methods: {
    send() {
      this.$emit("value", this.value);
    },
  },
};
</script>
//父组件
<template>
  <div class="panel">
    <label for="father">父组件</label>
    <input name="father" type="text" :value="msg" />
    <HelloWorld @value="getValue" />
  </div>
</template>

<script>
import HelloWorld from "@/components/HelloWorld.vue";
export default {
  name: "vue",
  components: { HelloWorld },
  data() {
    return {
      msg: "原来信息",
    };
  },
  methods: {
    getValue(val) {
      this.msg = val;
    },
  },
};
</script>

4.插槽

4.1插槽访问数据

需要访问传入子组件内部的prop,然后再根据prop编写具体的插槽模板,官方文档上有示例,但是之前学习的时候没有仔细,漏掉了。

首先在插槽出口绑定prop,过程和普通组件一样,但是针对具名插槽和默认插槽,二者获取prop的方式略有不同。

<!-- 两个组件-->
<div class="test1">
    <!--这是一个默认插槽(没有提供name属性,它作为vue保留属性,不会传入到组件内部,没有提供name的皆为默认插槽)-->
    <slot title="标题"></slot>
</div>

<div class=test2>
    <!-- 这是具名插槽(拥有name属性作为区分) -->
    <slot name='footer' :date="2023-04-19"></slot>
</div>

针对默认插槽,接下来,在入口处获取prop,需要借助v-slot指令,该指令的简写:#。

<!-- 针对默认插槽 -->
<Test1 v-slot="slotProps">
  {{ slotProps.title }}
</Test1>

针对具名插槽,需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令:

<!-- 针对具名插槽 -->
<Test2 >
    <template #header="slotProps">
        {{ slotProps.data }}
    </template>
</Test2>

注意 如果一个组件同时使用默认插槽和具名插槽,那么必须为默认插槽提供template,并指定name为default

<SomeComponent>
    <template #default="slotProps">
        {{ slotProps.propName }}
    </template>
</SomeComponent>

4.2判断插槽是否使用

插槽提供了组件部分个性化定制的功能,那如果父组件没有传入插槽模板,如何设置默认内容呢?

1.定义插槽的同时编写默认模板。在定义插槽时,将默认模板写在插槽元素内部,当没有传入父组件没有传入插槽模板时,将渲染默认模板。

<div>
<slot name="slotName" :props="props">
<!-- 以下为默认模板 -->
<div class="default-content">
   {{ props.name }}
</div>
</slot>
</div>

2.借助$slots.default。在子组件内部获取该参数并进行判断,结合v-if指令进行渲染。

<div>
<slot v-if="$slots.default" :props="props"></slot>
<div v-else>
<!-- 默认内容 --> 
{{ props.name }}
</div>
</div>

5.组件

5.1父组件调用子组件方法

借助$refs语法糖,在组件上设置ref属性后,在代码里可以通过 this.$refs.ref值 访问到对应的子组件。

//==============================子组件==========================
<template>
    <div>
        我是子组件
    </div>
</template>
<script>
export default{
    methods:{
        func(p){
            return '这是参数p' + p
        }
    }
}
</script>
//这是vue2写法,但是在组件式vue3里也同样适用
//==============================父组件======================
<template>
    <sonChild ref="child"><sonChild>
</template>
<script>
    export default{
        methods:{
        // 调用sonChild子组件的func方法
            this.$refs.child.func('hello')
        }
    }
</script>
//==============================父组件,vue3写法======================
<template>
    <sonChild ref="child"><sonChild>
</template>
<script>
    export default{
        mounted:{
            const child = ref();
            child.value.func('hello')
        }
    }
</script>

5.2自动导入所有公共组件2

使用 require 提供的函数 context 加载某一个目录下的所有 .vue 后缀的文件。然后 context 函数会返回一个导入函数 importFn,它有一个属性 keys() 获取所有的文件路径,通过文件路径数组,通过遍历数组,再使用 importFn 根据路径导入组件对象,遍历的同时进行全局注册即可。

//=========index.js=================
// 导入components文件夹下的所有组件
// 批量导入需要使用一个函数 require.context(dir,deep,matching)
// 参数:1. 目录  2. 是否加载子目录  3. 加载的正则匹配
//匹配当前文件夹下的所有.vue文件 注册全局组件
const importFn = require.context('./', false, /\.vue$/)
// console.dir(importFn.keys()) 文件名称数组
export default {
  install (app) {
    // 批量注册全局组件
    importFn.keys().forEach(key => {
      // 导入组件
      const component = importFn(key).default
      // 注册组件
      app.component(component.name, component)
    })
  }
}
//========main.js===============
import publicComponents from '@/components/index'
createApp(App)
    .use(publicComponents)
    .mount('#app')

其他

添加环境变量

在项目根路径下创建配置文件.env文件

FOO=bar
VUE_APP_NOT_SECRET_CODE=some_value

访问变量

console.log(process.env.VUE_APP_SECRET)

包含三种配置文件

.env 不管是开发环境还是正式环境都会加载

.env.development 仅开发模式加载,即npm run serve

.env.production 仅正式环境加载,即npm run build

注意 .development和.production会覆盖.env内的同名变量,只有 NODE_ENVBASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中。

检测组件无效属性(方法,变量)

我发现项目中有些组件是其他同事复制别的项目过来的,直接在原先的业务逻辑上编写新的业务代码,但是模板部分被重构了,导致旧代码成为了无效代码。我不知道称之为“无效代码”是否合适,我的意思是指重构后这些定义在methods里的方法和定义在data里的变量,既没有在模板里引用,也没在其他方法里调用,它们在子组件里处于割裂的状态,对于组件功能来说,删除它们并无影响。

所以我开始手动删除,流程:对于data里的变量,仔细查找是否在模板、计算属性、监听器,函数内部等地方引用,若没有则将其删除;对于方法,复杂一点,除了检查组件内部调用之外,因为项目里对组件对外暴露属性这些细节没有规范化,有些涉及data操作的方法还要全局查找是否有外部组件对其调用。

上面的流程实在是太浪费时间,于是我寻找有没有外部工具能减少工作量,首先想到的是eslint,它能检查出在组件外部,及方法里定义且没有使用的变量,但对于定义在data里的变量和methods里的方法无能为力。后来在v站寻得一款工具——vue-hook-optimizer vscode插件,它能分析出组件内各种变量,方法的使用情况,形成一张关系图。

image-20230913143738327

同时输出提示信息……

image-20230913145134539

得到使用关系图后,按图索骥删除就行了,但是过程中还是要仔细些,因为插件检查出来的结果并不是百分百准确。


  1. https://juejin.cn/post/6887709516616433677 

  2. https://blog.csdn.net/weixin_42343307/article/details/120355310