Vue笔记(基于Vue2)

Vue笔记(基于Vue2)

笔记所用版本为Vue CLI v5.0.8,对个人开发项目中所用到的部分进行记录,除Vue外可能含部分其他技术的内容

查看下方目录来确定有无需要的内容



安装

Vue2,并带Router和Vuex

=>vue create 项目名称

=>Manually select features

=>Bable & Router & Vuex & Linter

=>2.x

=>n

=>ESLint with error prevention only

=>Lint on save

=>In package.json

=>根据需求选择是否保存该配置


目录构成

  1. src

    存放源代码

    • assets

    存放静态资源,例如引入的CSS,JS,图片

    • components

    存放项目组件,比如存放公用组件或拆分的view文件(防止原view内容过于庞大)

    • router(安装时选择Router)

    存放Rouer路由配置

    • store(安装时选择Vuex)

    存放Vuex状态管理库

    • views

    存放页面

    • App.vue

    文件为应用的根组件,应用的主要入口,可定义整体所需的布局、样式或全局行为

    • main.js

    文件为应用入口文件,用于创建Vue实例并挂载应用,引入全局配置和插件

  2. public

    存放无打包处理的静态资源,例如存放纯html文件或者固定的文件

  3. node_modules

    存放项目的模块,安装模块:npm install,因内容过于零碎与庞大,请上传时删除,由下载端自行安装

  4. dist

    存放打包好的静态网站,打包:npm run build,文件可直接放在服务器上使用

  5. package.json

    文件包含项目名称、版本、依赖等信息(依赖版本一般自动处理)

  6. vue.config.js

    文件用于配置Vue CLI构建工具,比如配置打包文件在本地打开而不是在服务器

  7. babel.config.js

    文件可将新版本的JavaScript转换为旧版本,以确保浏览器的兼容性(一般不用处理)


常用插件

Element UI(页面组件库)

页面组件库,Element UI

npm install element-ui

=>在main.js引用

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

Axios+Cookie(未经测试,并包含大量"测试"信息)

npm install axios

npm install js-cookie

推荐参考e4glet:Vue统一请求接口(axios),以下内容在此基础略有修改

=>axios封装

axios/index.js

import axios from 'axios';

// 创建全局实例
const instance = axios.create({
  baseURL: 'http://localhost:8080/api/', // 设置全局基础 URL
  timeout: 25000, // 设置全局超时时间
});

// 统一请求头配置
instance.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';

// 添加统一认证的请求头token信息
instance.interceptors.request.use(config => {
  config.headers.token = localStorage.getItem('token');
  return config;
});

// 添加统一拦截response响应,判断token是否过期,如过期则清空登录状态,返回登录页面
instance.interceptors.response.use(response => {
  if (response.data.state == 403) {
    localStorage.clear();
    window.location.reload();
  }
  return response;
}, error => {
  if (error.response.data.state == 403) {
    localStorage.clear();
    window.location.reload();
  }
  return Promise.reject(error);
});

const axiosInstance = {
  baseURL: instance.defaults.baseURL,
  /** get 请求
   * @param  {接口地址} url
   * @param  {请求参数} params
   */
  get: function (url, params) {
    return new Promise((resolve, reject) => {
      instance.get(url, params)
        .then((response) => {
          resolve(response.data);
        })
        .catch((error) => {
          reject(error);
        });
    })
  },
  /** post 请求
   * @param  {接口地址} url
   * @param  {请求参数} params
   */
  post: function (url, params) {
    return new Promise((resolve, reject) => {
      instance.post(url, params)
        .then((response) => {
          resolve(response.data);
        })
        .catch((error) => {
          reject(error);
        });
    });
  },
  /**
   * post 请求 文件上传
   * @param  {接口地址} url
   * @param  {请求参数} formData
   */
  postFile: function (url, formData) {
    return new Promise((resolve, reject) => {
      instance({
        url: url,
        method: 'post',
        data: formData,
        headers: { 'Content-Type': 'multipart/form-data' },
      }).then((response) => {        
        resolve(response.data);
      }).catch((error) => {
        reject(error);
      });      
    });
  },
  /**
   * post 请求 文件上传 带进度条
   * @param  {接口地址} url
   * @param  {请求参数} formData
   * @param  {请求进度} progress
   */
  postFileProgress: function (url, formData, progress) {
    return new Promise((resolve, reject) => {
      instance({
        url: url,
        method: 'post',
        data: formData,
        headers: { 'Content-Type': 'multipart/form-data' },
        onUploadProgress: progress,
      }).then((response) => {        
        resolve(response.data);
      }).catch((error) => {
        reject(error);
      });
    });
  },
}

export default axiosInstance;

=>全局注册

service/index.js

import Vue from 'vue'
import axiosInstance from '@/axios';

// 定义全局变量$axios
Vue.prototype.$axios = axiosInstance;

main.js

import '@/service'

=>使用(参考上面的接口提示)

this.$axios.get('your/api/endpoint', { params: { key1: 'value1', key2: 'value2' } })
    .then(response => {
      // 处理成功的回调
      console.log(response);
    })
    .catch(error => {
      // 处理错误的回调
      console.error(error);
    });
this.$axios.post('your/api/endpoint', postData)
    .then(response => {
      // 处理成功的回调
      console.log(response);
    })
    .catch(error => {
      // 处理错误的回调
      console.error(error);
    });

=>基础用法

GET

axios.get('https://localhost:8080/get')
  .then(response => {
    // 请求成功时的处理
    console.log('Response:', response.data);
  })
  .catch(error => {
    // 请求失败时的处理
    console.error('Error:', error);
  });

POST

const postData = {
  id: '1',
  name: 'hello',
};
axios.post('https://localhost:8080/post', postData)
  .then(response => {
    console.log('Response:', response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

DELETE

// 方式1:模板字符串
axios.delete(`https://localhost:8080/delete/${id}`)
// 后端演示
// @DeleteMapping("/delete/{id}")
// public String deleteResourceWithPathVariable(@PathVariable int id) {
//     return "Deleted resource with ID " + resourceId;
// }
// 方式2:查询参数
axios.delete(`https://localhost:8080/delete/id=${id}`)
// axios.delete('https://localhost:8080/delete?id='+id)
// 后端演示
// @DeleteMapping("/delete")
// public String deleteResourceWithRequestParam(@RequestParam int id) {
//     return "Deleted resource with ID " + id;
// }
  .then(response => {
    console.log('Response:', response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

PUT(全部更新)

const id = 1;
const putData = {
  title: 'title',
  body: 'body',
};
axios.put(`https://localhost:8080/put/${id}`, putData)
  .then(response => {
    console.log('Response:', response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

PATCH(部分更新)

const id = 1;
const patchData = {
  body: 'body',
};
axios.patch(`https://localhost:8080/patch/${id}`, patchData)
  .then(response => {
    console.log('Response:', response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

=>参考:如果不进行全局配置,使用配置文件进行全局配置

axios-config.js

import axios from 'axios';

// 创建全局实例
const instance = axios.create({
  baseURL: 'https://api.example.com', // 设置全局基础 URL
  timeout: 25000, // 设置全局超时时间
});

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 请求发送之的全局配置
      config.headers['Content-Type'] = 'application/json'; // 设置Content-Type为 application/json
    config.withCredentials = true; // 启用跨域携带cookie
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

// 响应拦截器
instance.interceptors.response.use(
  response => {
    // 响应返回之前的全局处理
    return response;
  },
  error => {
    return Promise.reject(error);
  }
);

export default instance;

main.js

import axiosInstance from './axios-config';

Vue.prototype.$axios = axiosInstance;

使用

this.$axios.get('/some-endpoint')
  .then(response => {
    // 处理成功的响应
    console.log('Response:', response.data);
  })
  .catch(error => {
    if (axios.isCancel(error)) {
      // 请求被取消时报错(可能因为超时,也可能是主动取消)
      console.error('Request canceled:', error.message);
    } else {
      // 处理其他错误
      console.error('Error:', error.message);
    }
  });

i18n(多语言)

网站多语言/国际化

npm install vue-i18n@8.28.2

=>在src下创建i18n文件夹

=>创建index.js和需要的语言文件(zh-CN.json、en.json等)

=>index.js

// author:e4glet
// 引入 vue
import Vue from 'vue';
// 引入 vue-i18n
import VueI18n from 'vue-i18n'
// 注册 vue-i18n
Vue.use(VueI18n)

// 利用 webpack 的 require.context 方法遍历读取 i18n 目录下的所有 .json 语言文件
// 这样做省去了逐一引入语言文件的工作量,只需要将创建的语言文件 xx.json 直接放入 i18n这个目录中就行
function loadLocaleMessages() {
   const locales = require.context('.', true, /.json$/i)
   const messages = { };
   locales.keys().forEach(key => { 
      messages[key.replace('./', '').replace('.json', '')] = locales(key);
   });
  // 返回语言配置对象
   return messages;
}
// 初始化语言配置
const i18nConfig = {
    locale: 'en',
    messages:loadLocaleMessages()
}

// 创建 vue-i18n 实例
const i18n = new VueI18n(i18nConfig)
// 导出实例
export default i18n;

=>语言.json

{
   "index.title": "Hello World",
   "index.more": "more information"
}

=>在main.js引用

import i18n from './i18n';

new Vue({
  i18n,
  // others
}).$mount('#app')

=>可选:修改xxx.vue实现手动配置语言

<template>
  <div class="home">
    <div>标题:{{ $t('index.title') }}</div>
    <div @click="setLang('en')">切换英语</div>
    <div @click="setLang('zh-Hans')">切换中文</div>
  </div>
</template>
<script>
export default {
  methods: {
    // 点击切换语言
    setLang(value) {
      this.$i18n.locale = value;
    }
  }
}
</script>

=>可选:修改App.vue实现自动配置语言

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
export default {
  mounted() {
    // 通过浏览器自动切换国际化语言版本
    let lang = navigator.language;
    if (lang) {
      this.$i18n.locale = lang;
    } else {
      this.$i18n.locale = 'en'
    }
  },
}
</script>

=>使用

<template>
  <div class="home">
    {{ $t('index.title') }}
  </div>
</template>

<script>
export default {
  methods: {
    data() {
      return {
        more: $t('index.more')
      }
    },
  },
}
</script>

moment(时间日期)

时间日期库

npm install moment

=>在main.js引用

import moment from 'moment';
Vue.prototype.$moment = moment;

=>使用

<template>
  <div class="home">
    {{ currentDate }}
  </div>
</template>

<script>

export default {
  components: {
    data() {
      return {
        // 转化为需要的格式,并作为String输出
        currentDate: this.$moment().format('YYYY-MM-DD HH:mm:ss'),
      }
    },
  }
}
</script>

UUID(通用唯一标识符)

生成不重复的随机UUID

可选版本为v1-v5

v1、v2:基于时间和节点生成(v2已被弃用)

v3、v5:基于名字和命名空间,前者为MD5后者为SHA1,需传递两个参数:name和namespace(UUID格式)(如果二者与之前使用的参数相同则会重复),如果需要根据已有信息延伸可以使用

v4:全随机生成,一般无特殊需求可直接使用

npm install uuid

=>在main.js引用

import { v4 as uuidv4, v5 as uuidv5 } from 'uuid';
Vue.prototype.$uuidv4 = uuidv4;
Vue.prototype.$uuidv5 = uuidv5;

=>使用

<template>
  <div class="home">
    <div>UUID v4: {{ uuidv4 }}</div>
    <div>UUID v5: {{ generateUuidv5('name', '一段UUID,不是这个格式会报错') }}</div>
  </div>
</template>

<script>

export default {
  data() {
    return {
      uuidv4: this.$uuidv4(),
    }
  },
  methods: {
    generateUuidv5(name, namespace) {
      return this.$uuidv5(name, namespace);
    },
  },
}
</script>

jQuery

[不常用]简化DOM操作和处理事件的JavaScript库

虽然特殊情况可能需要混合二者使用,但因为实现方法(Vue为数据驱动,jQuery为操作DOM)、设计理念、性能、组件化、生态等原因请尽量避免使用

npm install jquery

=>在main.js引用

import $ from 'jquery';
Vue.prototype.$ = $;

new Vue({
  $,
  // others
}).$mount('#app')

=>使用

<template>
  <div class="home">
    <button @click="useJQuery">Use jQuery</button>
  </div>
</template>

<script>
export default {
  methods: {
    useJQuery() {
      this.$('button').css('background-color', 'lightblue');
    }
  }
};
</script>

基础

初始样式CSS

用于清空网页基础的页面宽高保留

=>assets->css->index.css

html {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;
}

* {
  border: 0;
  padding: 0;
  margin: 0;
  text-decoration: none;
}

App.vue

<script>
import "@/assets/css/index.css";
</script>

简洁的App.vue

视图方面只负责视图容器,不进行任何页面相关的处理(使用其他页面或组件控制跳转)

样式方面可包含基础样式import "@/assets/css/index.css";

方法方面可包含挂载mounted和创建时created

=>App.vue

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
import "@/assets/css/index.css";

export default {
  name: 'App',
  mounted() {
  },
  created() {
  },
}
</script>

<style>
/* default */
</style>

组件化

尽量将公用组件独立为组件,如果需要也要将过于庞大的页面完全拆分

演示为使用一个简单的顶部组件

=>HomeView.vue

<template>
  <div class="home">
    <el-header>
      <BasicTop></BasicTop>
    </el-header>
  </div>
</template>

<script>
import BasicTop from '@/components/BasicTop.vue'

export default {
  components: {
    BasicTop
  },
}

</script>

=>BasicTop.vue

<template>
  <el-row :gutter="20" style="margin-left: 200px">
    <el-col :offset="1" :span="12" style="text-align: left">
      Hello World
    </el-col>
  </el-row>
</template>

基础指令

// 安装node-modules
npm install
// 启动服务器
npm run serve
// 将项目打包
npm run build

export default

export default {
  // name:组件名称
  name: 'MyComponent',
  // data:数据
  data() {
    return {
      propA: 'propA',
      deepObject: {
        propA: 'deepPropA',
        propB: 'deepPropB',
      },
    };
  },
  // computed:计算属性
  computed: {
    computedProp() {
      return this.propA + ' computed';
    },
  },
  // methods:包含组件方法
  methods: {
    someFunc() {
    },
  },
  // props:包含组件接收的属性
  props: {
    propertyName: {
      type: String,
      default: '',
    },
    propertyName2: String,
  },
  // watch:监视器
  watch: {
    'propertyName': function (newValue, oldValue) {
      console.log(`'propertyName' 从 ${oldValue} 变为 ${newValue}`);
    },
    deepObject: {
      handler: function (newVal, oldVal) {
        console.log('deepObject changed:', newVal);
      },
      deep: true,
    },
  },
  // components:子组件,配合import ChildComponent from '@/components/ChildComponent.vue'
  components: {
    // 子组件
    ChildComponent,
  },

  // 生命周期类
  // beforeCreate:生命周期钩子,实例创建之前调用
  // 实例初始化,数据观测前,事件配置前
  // 无data和methods
  beforeCreate() {
    console.log('beforeCreate');
  },
  // [常用]created:生命周期钩子,在实例创建完成后调用
  // 实例创建完成
  // 有data和methods
  created() {
    console.log('created');
  },
  // beforeMount:生命周期钩子,在挂载前调用
  // 模板渲染成虚拟DOM前
  beforeMount() {
    console.log('beforeMount');
  },
  // [常用]mounted:生命周期钩子,在挂载完成后调用
  // DOM操作,调用第三方库,异步请求
  mounted() {
    console.log('mounted');
  },
  // beforeUpdate:生命周期钩子,在更新前调用
  // 类比beforeMount
  // 可获取更新前状态
  beforeUpdate() {
    console.log('beforeUpdate');
  },
  // updated:生命周期钩子,在更新完成后调用
  // 类比mounted
  // 获取更新后状态
  updated() {
    console.log('updated');
  },
  // [常用]beforeDestroy:生命周期钩子,在销毁前调用
  // 实例销毁前
  // 清理定时器,取消监听,清理可能泄露的资源
  beforeDestroy() {
    // 取消监听
    // 一般Vue.js会自动取消所有的监听,除非过于复杂造成内存泄露
    this.$nextTick(() => {
      this.$watch('deepObject', null);
    });
    console.log('beforeDestroy');
  },
  // destroyed:生命周期钩子,在销毁完成后调用
  // 实例销毁后
  // 清理工作,释放资源
  destroyed() {
    console.log('destroyed');
  }
};

仅在本地运行(不使用服务器运行)

由于特殊情况(比如暂时没有服务器框架测试(比如Nginx)),或只想在本地运行静态网页

如果不添加此项,直接打开打包好的网页,网页会为一片空白

=>vue.config.js

// others
module.exports = defineConfig({
  publicPath: './',
  // others
})

技巧

父子传递数据

父组件->子组件

=>FatherView.vue

<template>
  <div class="father">
    <ChildView :messageFromParent="messageToChild" />
  </div>
</template>

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

export default {
  components: {
    ChildView,
  },
  data() {
    return {
      messageToChild: 'Hello from parent!',
    };
  },
};
</script>

=>ChildView.vue

<template>
  <div class="child">
    <p>{{ messageFromParent }}</p>
  </div>
</template>

<script>
export default {
  props: {
    messageFromParent: String,
  },
};
</script>

子组件->父组件

[笔者注:总感觉子传父复杂的一匹]

=>ChildView.vue

<template>
  <div class="child">
    <button @click="sendMessageToParent">发送消息给父组件</button>
  </div>
</template>

<script>
export default {
  methods: {
    sendMessageToParent() {
      // 在点击按钮时,通过 $emit 发送自定义事件 'messageToParent',并传递一个数据
      this.$emit('messageToParent', 'Hello from child!');
    },
  },
};
</script>

=>FatherView.vue

<template>
  <div class="father">
    <ChildView @messageToParent="receiveMessageFromChild" />
    <p>从子组件接收到的消息: {{ messageFromChild }}</p>
  </div>
</template>

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

export default {
  components: {
    ChildView,
  },
  data() {
    return {
      messageFromChild: '',
    };
  },
  methods: {
    // 父组件中的方法,用于接收来自子组件的消息
    receiveMessageFromChild(message) {
      this.messageFromChild = message;
    },
  },
};
</script>

Vuex

集中式状态管理库,介绍加在下代码块中

=>store->index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
import moment from 'moment';

export default new Vuex.Store({
  state: {
    // 状态,存储状态(数据)
    // 使用1:this.$store.state.变量名
    // 使用2:
    // import { mapState } from 'vuex';
    // export default {
    //   computed: {
    //     ...mapState(['变量名']),
    //   },
    // },
    // 方法2注释过长,使用方法下同,不再赘述
    count: 0,
  },
  getters: {
    // 获取器,从state派生出的get方法
    // 使用:this.$store.getters
    doubleCount(state) {
      return state.count * 2;
    },
  },
  mutations: {
    // 变更,修改state的方式
    // 使用:this.$store.commit
    increment(state) {
      state.count++;
    },
  },
  actions: {
    // 动作,类似mutations,但是提交mutations而不是直接变更state,可以包含异步
    // 使用:this.$store.dispatch
    asyncIncrement(context) {
      // 异步操作,此处调用延迟1秒
      setTimeout(() => {
        // context.state/getters/commit/dispatch
        // 此处调用mutation
        context.commit('increment');
      }, 1000);
    },
  },
  modules: {
    // 模块,允许将state分割成模块,每个模块拥有上述的四种使用
  }
})

calc计算CSS属性(在任意标签内)

通过calc进行实时计算,并可传递已有数据

100vh为100%显示窗口高度,100vw为100%显示窗口宽度

<div :style="{ height: `calc(100vh - ${headerHeight})` }">
</div>

定期/延期执行

mounted() {
  // 定时执行
  // 此处分为需要清理和无需清理,按需选择,下延迟执行同理,不再赘述
  // 方法1:简易执行
  // 简易执行如果出现作用域问题,使用箭头函数版本
  setInterval(
    this.someFunc
  , 1000);
  // 方法2:箭头函数
  setInterval(() => {
    this.someFunc();
  }, 1000);
  //方法3:设定方法名以供清理定时器
  this.someFuncInterval = setInterval(() => this.someFunc(), 1000);

  // 延迟执行,一次
  this.someFuncTimeout = setTimeout(() => this.someFuncOnce(), 20000);

  // 清理定时器
  if (someCondition) {
    clearInterval(this.someFuncInterval);
    clearTimeout(this.someFuncTimeout);
  }
}

简易当前时间

export default {
  data() {
    return {
      currentTime: this.$moment().format('YYYY-MM-DD HH:mm:ss'),
    }
  },
  methods: {
    currentTimeFunc() {
      this.currentTime = this.$moment().format('YYYY-MM-DD HH:mm:ss')
    },
  },
  mounted() {
    setInterval(
      this.currentTimeFunc
      , 1000)
  }
}

posted @ 2024-01-22 17:38  meng25290  阅读(73)  评论(0)    收藏  举报