前端工作中问题点拆分

目录

1、路由:

vue组件中使用路由:

2、组件的封装使用

3、vue项目结构各项简介

①src——[项目核心文件]

②index.html——[主页]

③main.js——[入口文件]

package.json

vue.config.js

4、Vue组件化开发

5、插槽

插槽的基本类型:

插槽的用法概念:

1、默认插槽

后备内容:

2、具名插槽

3、作用域插槽:

4、动态插槽名:

5. Vue 2.6.0之前与Vue 2.6.0后的比对

5.1 默认插槽 缩写(由不写变成v-slot)

5.2 具名插槽 缩写 (由slot变成 v-slot)

5.3 作用域插槽 缩写 (由slot变成 v-slot)

5.4、插槽中数据传递方式

6、Vuex

6.1 安装

6.2  store文件夹的创建

6.3 引入store

6.4 各个状态的核心概念介绍

6.4.1 state:

 使用方式

6.4.2 mutations

使用方式

6.4.3  actions

使用方式:

6.4.4  getters

6.5  模块化编码(modules)

7、自定义指令

8、混入(Mixins)

9、渲染函数


1、路由:

关于项目中的路由,有一篇文章写的十分精彩:后台管理系统权限处理_后台管理的权限及路由分级-CSDN博客文章浏览阅读702次,点赞10次,收藏11次。菜单权限:根据登录角色的不同,用户拥有的菜单权限、按钮权限不同;其中,超级管理员具有所有的菜单、按钮权限,其他角色不定。_后台管理的权限及路由分级https://blog.csdn.net/qq_41604686/article/details/135393866https://blog.csdn.net/qq_41604686/article/details/135393866
官网对于路由的详解:路由懒加载 | Vue RouterVue.js 的官方路由https://router.vuejs.org/zh/guide/advanced/lazy-loading.htmlhttps://router.vuejs.org/zh/guide/advanced/lazy-loading.html

关于路由模式会出现的问题以及解决方法:https://www.nzw6.com/17060.htmlhttps://www.nzw6.com/17060.htmlhttps://www.nzw6.com/17060.html

路由懒加载:

        路由懒加载,也叫延迟加载或按需加载,是在需要的时候进行加载的一种技术。在单页面应用(SPA)中,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了loading也不利于用户体验。而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。这可以通过使用 Vue 的异步组件和 Webpack 的代码分割功能来实现。

vue组件中使用路由:

在 Vue 组件中,你可以使用 `this.$router` 访问路由实例,使用 `this.$route` 访问当前路由。

<template>
  <div>
    <h1>User {
  
  { $route.params.id }}</h1>
    <button @click="goBack">Go Back</button>
  </div>
</template>
 
<script>
export default {
  methods: {
    goBack() {
      this.$router.go(-1); // 使用路由实例的方法回到上一页
    }
  }
};
</script>

2、组件的封装使用

局部组件:

在对应页使用components注册组件。

<template>
  <count-to :startVal='startVal' :endVal='endVal' :duration='3000'></count-to>
</template>

<script>
import countTo from 'vue-count-to';
export default {
  components: { countTo },
  data () {
    return {
      startVal: 0,
      endVal: 2020
    }
  }
}
</script>

全局注册:

在 main.js文件下注册挂载组件。

import countTo from 'vue-count-to'
Vue.component('countTo', countTo)
<template>
  <count-to :startVal='startVal' :endVal='endVal' :duration='3000'></count-to>
</template>

3、vue项目结构各项简介

├── build --------------------------------- 项目构建(webpack)相关配置文件,配置参数什么的,一般不用动 
│   ├── build.js --------------------------webpack打包配置文件
│   ├── check-versions.js ------------------------------ 检查npm,nodejs版本
│   ├── dev-client.js ---------------------------------- 设置环境
│   ├── dev-server.js ---------------------------------- 创建express服务器,配置中间件,启动可热重载的服务器,用于开发项目
│   ├── utils.js --------------------------------------- 配置资源路径,配置css加载器
│   ├── vue-loader.conf.js ----------------------------- 配置css加载器等
│   ├── webpack.base.conf.js --------------------------- webpack基本配置
│   ├── webpack.dev.conf.js ---------------------------- 用于开发的webpack设置
│   ├── webpack.prod.conf.js --------------------------- 用于打包的webpack设置
├── config ---------------------------------- 配置目录,包括端口号等。我们初学可以使用默认的。
│   ├── dev.env.js -------------------------- 开发环境变量
│   ├── index.js ---------------------------- 项目配置文件
│   ├── prod.env.js ------------------------- 生产环境变量
│   ├── test.env.js ------------------------- 测试环境变量
├── node_modules ---------------------------- npm 加载的项目依赖模块
├── src ------------------------------------- 我们要开发的目录,基本上要做的事情都在这个目录里。
│   ├── assets ------------------------------ 静态文件,放置一些图片,如logo等
│   ├── components -------------------------- 组件目录,存放组件文件,可以不用。
│   ├── main.js ----------------------------- 主js
│   ├── App.vue ----------------------------- 项目入口组件,我们也可以直接将组件写这里,而不使用 components 目录。
│   ├── router ------------------------------ 路由
├── static ---------------------------- 静态资源目录,如图片、字体等。
├── index.html ------------------------------ 	首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
├── package.json ---------------------------- node配置文件,记载着一些命令和依赖还有简要的项目描述信息 
├──  .README.md------------------------------- 项目的说明文档,markdown 格式。想怎么写怎么写,不会写就参照github上star多的项目,看人家怎么写的
├── .xxxx文件:这些是一些配置文件,包括语法配置,git配置等
│   ├── .babelrc--------------------------------- babel配置文件
│   ├──  .editorconfig---------------------------- 编辑器配置
│   ├──  .eslintignore------------------------------- 配置需要或略的路径,一般build、config、dist、test等目录都会配置忽略
│   ├──  .eslintrc.js ------------------------------- 配置代码格式风格检查规则
│   ├──  .gitignore------------------------------- 配置git可忽略的文件
│   ├──  .postcssrc.js ------------------------------- css转换工具
①src——[项目核心文件]

在vue-cli的项目中,其中src文件夹是必须要掌握的,因为基本上要做的事情都在这个目录里

②index.html——[主页]

index.html如其他html一样,但一般只定义一个空的根节点,在main.js里面定义的实例将挂载在根节点下,内容都通过vue组件来填充

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuedemo</title>
  </head>
  <body>
      <!-- 定义的vue实例将挂载在#app节点下 -->
    <div id="app"></div>
  </body>
</html>
 
③main.js——[入口文件]

main.js主要是引入vue框架,根组件及路由设置,并且定义vue实例,下面的 components:{App}就是引入的根组件App.vue

后期还可以引入插件,当然首先得安装插件

/*引入vue框架*/
import Vue from 'vue'
/*引入根组件*/
import App from './App'
/*引入路由设置*/
import router from './router'
 
/*关闭生产模式下给出的提示*/ 
Vue.config.productionTip = false
 
/*定义实例*/ 
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})
package.json

package.json文件提供了很多项目相关的信息,主要有这个项目所需要的各种模块;以及项目的配置信息(比如名称、版本、许可证等元数据);还可以配置一些简化script执行脚本。项目中json文件是不能添加注释的,需要删除相关注释噢

{
  "name": "vue-project", // 项目的名称
  "version": "0.1.0", // 项目的版本号  大版本号.小版本号.修订版本号[.日期版本号]
  "private": true, // 是否对外开放,private为true表示不对外开放
  "scripts": {
    // script配置脚本对象,表示npm run XXX
    "serve": "vue-cli-service serve", // 配置serve脚本,表示npm run serve 等同于 vue-cli-service serve 命令
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    // 依赖的相关信息,这里主要是生产和开发依赖,一般用npm install XXX --save 安装的依赖就会添加到这里
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    // 开发依赖的相关信息,这里的主要是开发过程的依赖,生产环境中不会存在,一般用 npm install XXX --save-dev 安装的依赖会添加到这里
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^6.2.2",
    "lint-staged": "^9.5.0",
    "node-sass": "^4.12.0",
    "prettier": "^2.2.1",
    "sass-loader": "^8.0.2",
    "vue-template-compiler": "^2.6.11"
  }
}
vue.config.js
const path = require('path') // 导入Node的path模块
 
// 解析函数,在配置引入别名时用到
function resolve(dir) {
  return path.join(__dirname, dir)
}
 
// vue.config.js的主体配置
module.exports = {
  publicPath: '/', // 部署应用包时的基本 URL。用法和 webpack 本身的 output.publicPath 一致。
  outputDir: 'dist', // 当运行 vue-cli-service build 时生成的生产环境构建文件的目录。
  assetsDir: 'assets', // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。
  css: {
    // 对css的一些配置
    extract: true, // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)。
    sourceMap: false // 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能。
  },
  lintOnSave: process.env.NODE_ENV === 'development', // 是否在开发环境下通过 eslint-loader 在每次保存时 lint 代码。这个值会在 @vue/cli-plugin-eslint 被安装之后生效。
  devServer: {
    // 服务相关的设置
    host: '127.0.0.1', // 指定一个主机名
    port: 8000, // 指定一个端口号
    open: true, // 运行成功后是否自动打开页面
    proxy: {
      // 代理相关。如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器
      '/api': {
        // 对请求接口中出现的api进行代理
        target: process.env.VUE_APP_PROXY_URL, // 代理的目标地址,这个取值在后面的文件会讲到
        changeOrigin: true, // 是否改变域名,
        ws: false, // 是否开启webSocket
        pathRewrite: {
          // 路径重写,如果默认不重写路径,那么`/api/users`会被代理到`target路径/api/users`
          '^/api': '' // 对api进行路径重写,重写后,那么`/api/users`会被代理到`target路径/users`
        }
      }
    }
  },
  // webpack相关的配置,可以设置plugins和别名解析等
  configureWebpack: {
    // 解析设置
    resolve: {
      // 别名配置,用来创建 import 或 require 的别名,来确保模块引入变得更简单。
      alias: {
        // 用'@'表示src的路径, @/views/Home.vue 等同于 src/views/Home.vue.
        '@': resolve('src'),
        // 同理,用@components 表示 src/components目录
        '@components': resolve('src/components'),
        '@assets': resolve('src/assets')
      }
    },
    // 配置webpack的plugins
    plugins: []
  },
  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
  productionSourceMap: false
}

4、Vue组件化开发

组件化开发可以进行组件封装,进行复用,提高开发效率,例如文件上传,图片上传与显示,甚至列表页或者详情页等等

【Vue】中el-table数据项扩展各种类型总结(持续更新)-CSDN博客文章浏览阅读5.9k次,点赞39次,收藏53次。在这个示例中,我们将el-link放置在el-table-column的作用域插槽中,并使用scope.row来访问每个数据项的属性。在这个示例中,我们将el-select放置在el-table-column的作用域插槽中,并使用scope.row来访问每个数据项的属性。在上面的例子中,我们使用了el-table-column来定义列,同时使用了slot-scope来访问每一行的数据。这样,我们就可以在el-table的数据项中使用动态的el-tag了,每个人的标签会根据不同的类型显示不同的样式。_el-tablehttps://libusi.blog.csdn.net/article/details/131668481https://libusi.blog.csdn.net/article/details/131668481

vue组件之间通信:

【Vue】父组件子组件的通信方式20种(全网最全总结)_vue父子组件通信-CSDN博客文章浏览阅读1.1w次,点赞44次,收藏124次。目录Props:父组件向子组件传递数据,子组件通过props属性接收数据父组件:子组件:$emit和$on:子组件向父组件传递数据,子组件通过$emit触发事件,父组件通过$on监听事件并接收数据。父组件:子组件:$parent和$children:父组件向子组件传递数据,父组件通过$children获取子组件实例并调用子组件方法传递数据。父组件:子组件:$attrs和$listeners:父组件向子组件传递属性和事件,子组件通过$attrs获取属性,通过$listeners获取事件并绑定在子组件上。父组件_vue父子组件通信https://blog.csdn.net/libusi001/article/details/131668644https://blog.csdn.net/libusi001/article/details/131668644

5、插槽

关于插槽这里有一篇文章写的也十分精彩:Vue插槽详解:内容传递、作用域与动态命名,-CSDN博客文章浏览阅读1.1k次,点赞21次,收藏15次。但在某些场景下插槽的内容可能想要,同时使用父组件域内和子组件域内的数据。子组件模板中的表达式只能访问子组件的作用域。插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。vue通过使用slot插槽实现。现在组件可以用在不同的地方渲染各异的内容,但同时还保证都具有相同的样式。插值表达式渲染的内容都是一样的,都是父组件作用域的变量message。在组件标签的标签体中,没有提供任何内容的情况下,可以为插槽指定默认内容。所处的作用域,这和 JavaScript 的词法作用域规则是一致的。_vue2 具名插槽https://blog.csdn.net/lpp09230129/article/details/134966394https://blog.csdn.net/lpp09230129/article/details/134966394

插槽的基本类型:

默认插槽、具名插槽、作用域插槽、动态插槽名、插槽后备内容


插槽的用法概念:


在Vue中,子组件的模板可以定义多个插槽(包括默认插槽和具名插槽等等),
而父组件在引用子组件时,可以根据需要 有选择性的为这些插槽插入内容。
如果父组件没有为某个插槽提供内容,那么子组件的模板中该插槽的位置将显示为该插槽的默认内容(如果有的话),或者简单地留空。

  简单来说插槽就是用于决定将所携带的内容,插入到指定的某个位置,举个例子,比如说下面的图,加入头部和底部都是写死的,但是我们希望主题部分不写死,那么这时候我们就需要用插槽了,也就是说插槽的作用就是让组件内部的一些结构支持自定义:

1、默认插槽

子组件中:

//fa.vue  子组件
<template>
  <div class="fa">
     <div class="header" style="border: 1px solid black;width: 200px; height: 200px;">  
        <h1>我是头部</h1>
        
     </div>
     <div class="main" style="border: 1px solid red; width: 200px; height: 200px;">
      <!-- 通过slot标签作为展示 -->
        <slot></slot>
     </div>
 
     <div class="footer" style="border: 1px solid blue; width: 200px; height: 200px;">
        <hr>
        <h1>我是底部</h1>
     </div>
 
 
  </div>
</template>
 
 

父组件中:

//父组件  App.vue
 
<script setup>
import fa from './components/fa.vue';
</script>
 
<template>
  <fa>我是动态插槽----</fa>
 
 
</template>
 
<style scoped>
 
</style>
后备内容:

封装组件时,可以为预留的<slot>插槽提供后备内容(默认内容)。

效果:

外部使用组件时,不传东西,则slot会显示后备内容

外部如果使用组件,传东西,则slot整体会被换掉,显示传的东西

例如,在子组件中:

 <div class="main" style="border: 1px solid red; width: 200px; height: 200px;">
      <!-- 通过slot标签作为展示 -->
        <slot><p>不传东西就显示我</p></slot>
     </div>

2、具名插槽
  • 定义:带有名称的插槽,用于接收父组件中明确指定插槽名称的内容。
  • 用法:在子组件中使用定义具名插槽,父组件中通过<template v-slot:插槽名称>或简写为<template #插槽名称>来指定内容应该插入哪个具名插槽。

例如,我想让头部主体和底部三部分都可以动态展示:

父组件中:

//App.vue   父组件
 
<template>
  <fa>
    <!-- 通过template配合,名字对应着标签,#head是简写,完整写法是:v-slot:head -->
  <template #head>
    我是头部
  </template>
<template #main>
我是中间的
</template>
<template #footer>
  我是下面的底部
</template>
  </fa>
 
 
</template>
 
 

子组件中:

//fa.vue   子组件
 
<template>
  <div class="fa">
     <div class="header" style="border: 1px solid black;width: 200px; height: 100px;">  
      <!-- 多个slot使用name属性区分名字 -->
       <slot name="head"></slot>
        
     </div>
     <div class="main" style="border: 1px solid red; width: 200px; height: 100px;">
      <!-- 通过slot标签作为展示 -->
        <slot name="main"></slot>
     </div>
 
     <div class="footer" style="border: 1px solid blue; width: 200px; height: 100px;">
       <slot name="footer"></slot>
     </div>
 
 
  </div>
</template>
 
<script setup>
 
</script>
 
<style>
 
</style>
 
 
3、作用域插槽:
  • 定义:一种特殊的插槽,允许子组件将数据暴露给父组件的插槽内容。
  • 其实如果将插槽分类的话,插槽只分成两种,分别是默认插槽和具名插槽,作用域插槽并不在其中。作用域插槽就是在定义slot插槽的同时,它是可以传值的,给插槽绑定数据,将来使用组件时可以用(父传子)

  • 用法
    在子组件中,通过<slot :数据名=“数据值”>将数据传递给插槽;
    在父组件中,通过<template v-slot:插槽名称=“slotProps”>接收数据,并使用slotProps来访问传递过来的数据。

(1)给slot标签,以添加属性的方式传值

<slot :id=item.id msg='测试'></slot>

(2)所有添加的属性都会被收集到一个对象中

{
id:3,
msg:'测试'
}

(3)在template中,通过'#插槽名=obj'接收,默认插槽名为default

 <fa :list="list">
    <template #default="obj">
        <button @click=del(obj.id)>删除</button>
    </template>
</fa>
4、动态插槽名:
  • 定义:允许插槽的名称是动态的,根据组件的状态或其他条件来决定使用哪个插槽。
  • 用法:在父组件中,通过:slot="动态名称"来绑定插槽的名称,其中动态名称可以是一个计算属性、方法返回值或数据属性。

举例说明:
这个例子稍微复杂一些,因为它通常用于更高级的场景,比如根据条件动态渲染不同的插槽。但基本思想是使用计算属性或方法来返回插槽名。我们在组件中添加了一个按钮,并在按钮的点击事件处理程序中切换currentSlot的值。当按钮被点击时,currentSlot的值会从default切换到custom,或者从custom切换到default,从而实现默认内容和自定义内容的切换。

子组件:

// 子组件 Com06.vue 
<template>
  <div>
    <button @click="toggleSlot">切换插槽</button>
    <slot :name="currentSlot"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentSlot: 'default666',
    };
  },
  methods: {
    toggleSlot() {
      this.currentSlot = this.currentSlot === 'default666' ? 'custom666' : 'default666';
    },
  },
};
</script>

父组件中:

// 父组件 App.vue
<template>
  <div>
    <Com06>
      <template v-slot:default666>
        <p>这是默认内容!</p>
      </template>
      <template v-slot:custom666>
        <p>这是自定义内容!</p>
      </template>
    </Com06>
  </div>
</template>

<script>
import Com06 from './components/Com06.vue'
export default {
  name: "App",
  components: {Com06},
}
</script>
5. Vue 2.6.0之前与Vue 2.6.0后的比对

e2.6.0及以后的版本中,Vue团队对插槽(slot)的语法进行了简化和改进,引入了v-slot指令来替代原有的slot和slot-scope语法

5.1 默认插槽 缩写(由不写变成v-slot)
父组件
<child-component><p>这是默认插槽的内容</p></child-component>

子组件
<template><slot></slot></template>

缩写后变成:

父组件(推荐使用<template>标签,但也可直接用于元素上)
<child-component>
<template v-slot><p>这是默认插槽的内容</p></template>
</child-component>

或(注意:直接在元素上使用v-slot较少见,且可能需要额外配置)
<child-component>
<p v-slot></p>
</child-component>

子组件不变
<template><slot></slot></template>
5.2 具名插槽 缩写 (由slot变成 v-slot)
父组件
<child-component>
<template slot="header"><h1>这是头部内容</h1></template>
<template slot="footer"><p>这是底部内容</p></template>
</child-component>

子组件
<template>
<slot name="header"></slot>
<slot name="footer"></slot>
</template>

缩写后变成:

父组件
<child-component>
<template v-slot:header><h1>这是头部内容</h1></template>
<template v-slot:footer><p>这是底部内容</p></template>
</child-component>

或简写
<child-component>
<template #header><h1>这是头部内容</h1></template>
<template #footer><p>这是底部内容</p></template>
</child-component>

子组件不变
<template>
<slot name="header"></slot>
<slot name="footer"></slot>
</template>
5.3 作用域插槽 缩写 (由slot变成 v-slot)
父组件
<child-component>
<template slot="item" slot-scope="slotProps">
<p>{
  
  { slotProps.text }}</p>
</template>
</child-component>

子组件
<template>
<slot name="item" :text="itemText"></slot>
</template>

缩写后变成

父组件
<child-component>
<template v-slot:item="slotProps">
<p>{
  
  { slotProps.text }}</p>
</template>
</child-component>

或简写
<child-component>
<template #item="slotProps">
<p>{
  
  { slotProps.text }}</p>
</template>
</child-component>

子组件不变
<template>
<slot name="item" :text="itemText"></slot>
</template>
5.4、插槽中数据传递方式

通过 作用域插槽 实现 子组件中的插槽如何传递给父组件用

举例:

<!-- 子组件中 将someFormObject这个对象作为form属性传递给插槽-->  
<slot name="form" :form="someFormObject">...</slot>


<!-- 父组件中 使用 slot-scope 属性 来接收form属性 -->  
<template slot="form" slot-scope="{ form }">


<!-- 2.6 版后缩写 语法为 v-slot:插槽名称="{ 属性名称}" -->  
<template v-slot:form="{ form }"> </template>  

归纳
插槽是Vue.js中非常重要的一个概念,它允许父组件向子组件的模板中插入内容,从而实现组件内容的分发和组合。
通过默认插槽、具名插槽、作用域插槽、动态插槽名、插槽后备内容以及插槽的简写语法等用法,Vue.js提供了灵活且强大的组件化解决方案。这些用法不仅提高了组件的复用性和灵活性,还降低了组件之间的耦合度,使得Vue.js应用更加易于开发和维护。

在实际开发中,根据具体的需求和场景选择合适的插槽用法,可以构建出高效、可维护的Vue.js应用。

6、Vuex

关于vuex的介绍还有一篇文章也是十分精彩:

1-vuex安装及详细使用教程-附案例源码-CSDN博客文章浏览阅读9.2k次,点赞10次,收藏21次。文章目录1. 为什么要用 vuex ?2. vuex 是什么?2.1 单向数据流2.1.1 单向数据流小结3. vuex 的工作流4. 安装 vuex5. 引入 vuex6. 从 Store 开始6.1 创建一个 Store6.2 实例6.2.1 初始化6.2.2 example016.2.2.1 example01-16.2.2.2 example01-26.2.2.3 example01-36.2.2.4 example01-46.2.2.5 example01-56.2.2.6 example01-_vuexhttps://blog.csdn.net/u013946061/article/details/107858369https://blog.csdn.net/u013946061/article/details/107858369

6.1 安装

npm i  -g vue@3

 注释:@3是版本号,vue2用vuex@3版本,vue3用4版本

6.2  store文件夹的创建

概念:每一个Vuex应用的核心就是Store(仓库),我们可以说Store是一个容器,Store里面的状态与单纯的全局变量是不一样的,无法直接改变Store中的状态。想要改变状态需通过mutation去修改。

在 src根目录下创建文件夹store,并在其下index.js。需包含actions,mutations,state结构如下:

// 引入vue
import Vue  from 'vue'
 
// 引入vuex
import Vuex from 'vuex'
 
// 应用vue插件
Vue.use(Vuex)
 
// actions响应组件中的动作
const actions = {
}
 
// mutations操作数据state
const mutations = {
}
 
// 准备state存储数据
const state = {
   //状态对象
}
 
// 创建store并导出
const store = new Vuex.Store({
        actions,
        mutations,
        state,
})
 
 
//默认导出store
export default store

6.3 引入store

在main.js中引入store,全局组件都可以使用vuex。

import store from './store'

new Vue({

  render: h => h(App),

  store,

}).$mount('#app')

6.4 各个状态的核心概念介绍

6.4.1 state:

state是状态数据,可以通过this.$store.state来直接获取状态,也可以利用vuex提供的

mapState辅助函数将state映射到计算属性(computed)中去。

const state = {
	sum:0,
	realname:'张三',
	age:19
}

 使用方式

 插值引用:{ {$store.state.sum}}

 使用计算属性

  computed:{
        // 使用计算属性表达
        title( ){
            return this.$store.state.title;
        },
        singer( ){
            return this.$store.state.singer;
        }
        
    },
使用mapState辅助函数

注意:使用辅助函数的前提是要先引入

// 引入mapState,mapGetters

import {mapState,mapMutations,mapActions,mapGetters} from 'vuex';
computed:{
    
        // 利用mapState辅助函数的简写形式,代替以上的语句,但本质也是生成计算属性
        ...mapState('options',['title','singer']),
       
    },

6.4.2 mutations

更改 Vuex 的 store 中的修改状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

// 操作state中的数据
mutations :{
    editTitle(state,value){
        state.title = value;
    },
    editSinger(state,value){
        state.singer = value;
    }
},

使用方式

使用methods方法,使用commit操作state中的数据

   <button @click="editTitle('Lover')">更改歌名</button>
 
   更改歌手名字
    editTitle(str){
        this.$store.commit('editTitle',str);
    },

使用mapMutations辅助函数

 // 利用mapMutation辅助函数的简写形式
    ...mapMutations('options',['editTitle','editSinger']),

6.4.3  actions

Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。

   // 相应组件中的动作,组件中可以通过通过dispatch发送,
// actions 通过commit发送动作告知mutations
 actions:{
    changeSing(context,value){
        setTimeout(()=>{
            context.commit('editTitle',value);
                },3000)
    },
    changeSinger(context,value){
        setTimeout(()=>{
            context.commit('editSinger',value);
                },3000)
    }
},

使用方式:

使用methods方法

 <button @click="changeSing('EndGame')">三秒后更改歌名</button>
 
三秒后更改歌名
    changeSing(str){
        setTimeout(()=>{
            this.$store.dispatch('changeSing',str);
        },3000)
    },
     changeSinger(str){
        setTimeout(()=>{
            this.$store.dispatch('changeSinger',str);
        },3000)
    }

使用mapActions辅助函数

 // 使用mapActions辅助函数简写形式
    ...mapActions(['changeSing','changeSinger'])

6.4.4  getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数,也就是对状态在加工:

// 依赖并加工state中的数据,state中的数据也会相应改变
 getters :{
   otherSong(state){
        return state.title = 'Delicate'
    }
}

使用mapGetters辅助函数

 // 利用mapGetters辅助函数的简写形似
        ...mapGetters('options',['otherSong'])

6.5  模块化编码(modules)

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割在模块中使用:namespaced: true, 命名空间,添加之后,当前模块下的标识符可以和其它模块相同,用于解决不同模块的命名冲突问题。

示例代码:

import Vue from 'vue';
 
import Vuex from 'vuex';
 
Vue.use(Vuex);
 
const options ={
    namespaced: true,
 
    // 相应组件中的动作,组件中可以通过通过dispatch发送,
// actions 通过commit发送动作告知mutations
 actions:{
    changeSing(context,value){
        setTimeout(()=>{
            context.commit('editTitle',value);
                },3000)
    },
    changeSinger(context,value){
        setTimeout(()=>{
            context.commit('editSinger',value);
                },3000)
    }
},
// 操作state中的数据
mutations :{
    editTitle(state,value){
        state.title = value;
    },
    editSinger(state,value){
        state.singer = value;
    }
},
// 管理store中的数据
state :{
    title:'Daylight',
    singer:'TaylorSwift'
},
// 依赖并加工state中的数据,state中的数据也会相应改变
 getters :{
   otherSong(state){
        return state.title = 'Delicate'
    }
}
 
}
const store = new Vuex.Store({
   modules:{
   options
   }
})
export default store;

请注意,以上代码仅为展示 Vuex 的基本用法。在实际开发中,你可能需要使用更多高级特性,如插件、严格模式等。此外,对于大型应用,建议将 store 分割成模块,并且可能需要结合 webpack 的代码分割功能来进行懒加载。

7、自定义指令

为什么要自定义指令

在使用vue的时候 我们某些场景下仍然需要对普通 DOM 元素进行底层操作,
在vue中、组件渲染需要时间,获取DOM常需要搭配setTimeout、$nextTick使用
而自定义指令在使用时,更便捷。

指令注册

指令的注册命令:Vue.directive(key, directives[key])
使用:Vue.use()

全局注册

在我们常见的项目结构中、directives文件夹下,定义的index.js文件中,我们会对指令进行全局注册。

import MyDirective from './directive/myDirective' 
const directives = { 
  MyDirective 
}
export default { 
  install(app) {
    // 遍历、注册
   Object.keys(directives).forEach((key) => {    
    app.directive(key, directives[key])  
   })
  }
}
局部注册

在你自己组件或页面中,使用directives选项自定义指令

export default {
  directives: {
    myDirective: {
      inserted: function (el) {
        //
      }
    }
  }
}
使用
<input v-myDirective>

添加参数
v-myDirective="data" 传递数值给指令,这里的data可以是组件中的data数据,也可以是methods方法。

<div v-myDirective="{ color: 'white', text: 'hello!' }"></div>

app.directive('myDirective', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})

v-myDirective:click="clickHandle",传递参数click,这里可以通过[xx]的格式动态传递参数。
v-myDirective:click.top.bar="clickHandle" 传递修饰符top和bar。

注意:不推荐在组件上使用自定义指令、它会应用于组件的根结点、和透传 attributes 类似。

指令的钩子和参数

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode) {}
}

常见指令的封装

v-copy:内容复制到剪切板中
const copy = {
  bind(el, { value }) {
    el.$value = value
    el.handler = () => {
      if (!el.$value) {
        // 值为空的时候,给出提示。可根据项目UI仔细设计
        console.log('无复制内容')
        return
      }
      // 动态创建 textarea 标签
      const textarea = document.createElement('textarea')
      // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
      textarea.readOnly = 'readonly'
      textarea.style.position = 'absolute'
      textarea.style.left = '-9999px'
      // 将要 copy 的值赋给 textarea 标签的 value 属性
      textarea.value = el.$value
      // 将 textarea 插入到 body 中
      document.body.appendChild(textarea)
      // 选中值并复制
      textarea.select()
      const result = document.execCommand('Copy')
      if (result) {
        console.log('复制成功') 
      }
      document.body.removeChild(textarea)
    }
    // 绑定点击事件
    el.addEventListener('click', el.handler)
  },
  // 当传进来的值更新的时候触发
  componentUpdated(el, { value }) {
    el.$value = value
  },
  // 指令与元素解绑的时候,移除事件绑定
  unbind(el) {
    el.removeEventListener('click', el.handler)
  },
}
 
export default copy
v-longpress

实现一个用户长按鼠标左键或移动端单指长按,触发的事件

const longpress = {
  bind(el, binding) {
    if (typeof binding.value!== 'function') {
      throw new Error('Callback must be a function');
    }

    let pressTimer = null;

    // 鼠标(左键)按下、移动端按下 且 按下持续时间超过 1s 时,触发
    const start = (e) => {
      if (
        (e.type === 'mousedown' && e.button!== 0) ||
        (e.type === 'touchstart' && e.touches.length > 1)
      ) {
        return;
      }

      pressTimer = setTimeout(() => {
        binding.value(e);
      }, 1000);
    };
    // 鼠标(左键)抬起、移动端抬起 或 按下时间小于 1s 时,移除定时器
    const cancel = () => {
      if (pressTimer!== null) {
        clearTimeout(pressTimer);
        pressTimer = null;
      }
    };
    // 事件监听
    el.addEventListener('mousedown', start);
    el.addEventListener('touchstart', start);
    el.addEventListener('click', cancel);
    el.addEventListener('mouseout', cancel);
    el.addEventListener('touchend', cancel);
    el.addEventListener('touchcancel', cancel);
  },
  // 指令与元素解绑的时候,移除事件绑定
  unbind(el) {
    el.removeEventListener('mousedown', start);
    el.removeEventListener('touchstart', start);
    el.removeEventListener('click', cancel);
    el.removeEventListener('mouseout', cancel);
    el.removeEventListener('touchend', cancel);
    el.removeEventListener('touchcancel', cancel);
  },
};

export default longpress
v-waterMarker

页面增加水印

function addWaterMarker(str, parentNode, font, textColor) {
  // 水印文字,父元素,字体,文字颜色
  var can = document.createElement('canvas')
  parentNode.appendChild(can)
  can.width = 200
  can.height = 150
  can.style.display = 'none'
  var cans = can.getContext('2d')
  cans.rotate((-20 * Math.PI) / 180)
  cans.font = font || '16px Microsoft JhengHei'
  cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)'
  cans.textAlign = 'left'
  cans.textBaseline = 'Middle'
  cans.fillText(str, can.width / 10, can.height / 2)
  parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')'
}
 
const waterMarker = {
  bind: function (el, binding) {
    addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)
  },
}
 
export default waterMarker

v-focus:自动聚焦输入框

如果你希望在页面加载时,某个输入框自动获得焦点,可以创建一个 v-focus 指令。这个指令可以帮你省掉在 mounted 钩子里写 DOM 操作的麻烦。

// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
  // 当绑定元素插入到 DOM 中时
  inserted: function (el) {
    // 聚焦元素
    el.focus();
  }
});

使用方法:

<input v-focus>
v-color:动态改变元素颜色

有时候我们需要根据某些条件动态改变某个元素的颜色,v-color 指令就派上用场了。

// 注册一个全局自定义指令 v-color
Vue.directive('color', {
  // 当绑定元素插入到 DOM 中时
  bind: function (el, binding) {
    el.style.color = binding.value;
  }
});

使用方法:

<div v-color="'red'">这段文字将会是红色的</div>
v-debounce:防抖输入

在处理用户输入时,你可能希望减少输入触发的事件次数(比如搜索输入框),使用 v-debounce 指令可以轻松实现防抖功能。

Vue.directive('debounce', {
  bind: function (el, binding) {
    let timeout;
    el.addEventListener('input', () => {
      if (timeout) clearTimeout(timeout);
      timeout = setTimeout(() => {
        binding.value(el.value);
      }, 500); // 延迟时间可以根据需要调整
    });
  }
});

使用方法:

<input v-debounce="handleInput">

在你的 Vue 组件中:

methods: {
  handleInput(value) {
    console.log(value);
  }
}
 v-scroll:监听滚动事件

有时候我们需要在页面滚动时执行一些操作,比如加载更多数据或改变导航栏样式,可以使用 v-scroll 指令。

Vue.directive('scroll', {
  inserted: function (el, binding) {
    window.addEventListener('scroll', () => {
      binding.value(window.scrollY);
    });
  }
});

使用方法:

<div v-scroll="handleScroll">内容</div>

在你的 Vue 组件中:

methods: {
  handleScroll(scrollY) {
    console.log('滚动位置:', scrollY);
  }
}
v-resize:监听窗口大小变化

有时候我们需要动态调整元素的布局或样式以适应窗口的大小变化,v-resize 指令可以帮助我们简化这一操作。

Vue.directive('resize', {
  bind(el, binding) {
    const onResize = () => binding.value(el);
    window.addEventListener('resize', onResize);
    // 在元素卸载时移除事件监听器
    el._onResize = onResize;
  },
  unbind(el) {
    window.removeEventListener('resize', el._onResize);
  }
});

使用方法:

<div v-resize="handleResize">内容</div>

在你的 Vue 组件中:

methods: {
  handleResize(el) {
    console.log('元素尺寸:', el.clientWidth, el.clientHeight);
  }
}
v-lazyload:图片懒加载

懒加载是一种优化网页加载性能的技术,当用户滚动到图片所在位置时再加载图片,从而减少初始页面加载的资源消耗。我们可以创建一个 v-lazyload 指令来实现图片懒加载。

Vue.directive('lazyload', {
  bind(el, binding) {
    const loadImage = () => {
      const rect = el.getBoundingClientRect();
      if (rect.top < window.innerHeight && rect.bottom > 0) {
        el.src = binding.value;
        window.removeEventListener('scroll', loadImage);
      }
    };
    window.addEventListener('scroll', loadImage);
    loadImage(); // 立即执行一次,以防图片已在视口内
  },
  unbind() {
    window.removeEventListener('scroll', loadImage);
  }
});

使用方法:

<img v-lazyload="'path/to/image.jpg'" alt="图片描述">
v-clipboard:复制到剪贴板

为了在网页上提供一键复制文本到剪贴板的功能,我们可以创建一个 v-clipboard 指令。

Vue.directive('clipboard', {
  bind(el, binding) {
    el.addEventListener('click', () => {
      const textarea = document.createElement('textarea');
      textarea.value = binding.value;
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand('copy');
      document.body.removeChild(textarea);
      alert('复制成功!');
    });
  }
});

使用方法:

<button v-clipboard="'要复制的文本'">点击复制</button>
v-tooltip:简易提示框

有时候我们需要在鼠标悬停在某个元素上时显示一个提示框,这时可以用 v-tooltip 指令来实现。

Vue.directive('tooltip', {
  bind(el, binding) {
    const tooltip = document.createElement('div');
    tooltip.className = 'tooltip';
    tooltip.innerText = binding.value;
    tooltip.style.position = 'absolute';
    tooltip.style.backgroundColor = '#333';
    tooltip.style.color = '#fff';
    tooltip.style.padding = '5px 10px';
    tooltip.style.borderRadius = '4px';
    tooltip.style.display = 'none';
    document.body.appendChild(tooltip);

    el.addEventListener('mouseenter', () => {
      tooltip.style.display = 'block';
      const rect = el.getBoundingClientRect();
      tooltip.style.top = `${rect.top + window.scrollY - tooltip.offsetHeight}px`;
      tooltip.style.left = `${rect.left + window.scrollX}px`;
    });

    el.addEventListener('mouseleave', () => {
      tooltip.style.display = 'none';
    });
  },
  unbind(el) {
    el.removeEventListener('mouseenter');
    el.removeEventListener('mouseleave');
  }
});

使用方法:

<button v-tooltip="'这里是提示信息'">鼠标悬停显示提示</button>
v-permission:权限控制

在应用权限管理中,我们可能需要根据用户权限动态显示或隐藏某些内容,这时可以使用 v-permission 指令

Vue.directive('permission', {
  bind(el, binding, vnode) {
    const userPermissions = vnode.context.$store.state.userPermissions; // 假设从 Vuex store 中获取用户权限
    if (!userPermissions.includes(binding.value)) {
      el.style.display = 'none';
    }
  }
});

使用方法:

<button v-permission="'admin'">仅管理员可见的按钮</button>
v-infinite-scroll:无限滚动加载

在做分页加载时,常常会用到无限滚动加载,可以使用 v-infinite-scroll 指令来实现这一功能。

Vue.directive('infinite-scroll', {
  bind(el, binding) {
    const options = {
      root: el,
      rootMargin: '0px',
      threshold: 1.0
    };

    const callback = (entries) => {
      if (entries[0].isIntersecting) {
        binding.value();
      }
    };

    const observer = new IntersectionObserver(callback, options);
    observer.observe(el.lastElementChild);

    el._observer = observer;
  },
  unbind(el) {
    if (el._observer) {
      el._observer.disconnect();
      delete el._observer;
    }
  }
});

使用方法:

<div v-infinite-scroll="loadMore">
  <div v-for="item in items" :key="item.id">{
  
  { item.name }}</div>
</div>

在你的 Vue 组件中:

methods: {
  loadMore() {
    // 加载更多数据的逻辑
  }
}
v-highlight:文本高亮

为了在页面上高亮显示某些特定的文本,可以使用 v-highlight 指令。

Vue.directive('highlight', {
  bind(el, binding) {
    const text = el.innerHTML;
    const query = binding.value;
    const highlightedText = text.replace(new RegExp(`(${query})`, 'gi'), '<span class="highlight">$1</span>');
    el.innerHTML = highlightedText;
  }
});

使用方法:

<div v-highlight="'关键字'">这里包含一些关键字文本内容</div>

在 CSS 文件中定义高亮样式:

.highlight {
  background-color: yellow;
}

8、混入(Mixins)

9、渲染函数

未完:https://libusi.blog.csdn.net/article/details/134849673?spm=1001.2014.3001.5502https://libusi.blog.csdn.net/article/details/134849673?spm=1001.2014.3001.5502https://libusi.blog.csdn.net/article/details/134849673?spm=1001.2014.3001.5502

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值