![718a76f97a4fc8e57376b892d6394ebc.png](https://i-blog.csdnimg.cn/blog_migrate/29080cc4413569dc5a165b8a5965e64f.png)
vue.js,这个货,就是尤大,写的这个,前端框架,网上文章,一大堆,什么教程都有,所以我就不讲那些教学的东西了,我只是举一些例子,让你更容易的去理解一些重点,而不是只会照葫芦画瓢,若举一而反不了三,那还是没有理解其中的一些原理。
![b03b57fbd21dbf88dedc8ea37e67a119.png](https://i-blog.csdnimg.cn/blog_migrate/3a8d8fc14f5e67ee1e6b7b407ad71e59.png)
vue.js 文档 https://cn.vuejs.org/v2/guide/ 写的是很容易上手,且不管你有没有看懂第一章,请把所有的内容都过一遍。当你把所有的内容过完了,你在上手的时候,遇到疑问,你就有印象:“这个在文档里好像有,我去瞜一眼”,“这我在文档的某一章节见到过,去确认一下”。换成其他的新上手的技术也是一样,文档先通读,不用管有没有懂,只要有印象,再回看的时候,能够迅速定位到就行。这货,就那点儿东西,首先,别把请求的东西算进来,那跟它没关系,请求就是请求。基本语法没啥好说的,至于组件,props,$emit,就俩东西,这俩盘好了,就是组件。props 你这么理解,你用js也好,用php也好,用java也好,你在定义一个类的时候,给这个类定义了一些成员变量,props 就好比这些成员变量,这些成员变量可以定义类型,可以给它默认值,通过构造函数给他们初始化赋值,当你实例化这个类的时候,需要有的成员变量必须传值,有的不需要。想一想你看的文档中或者你的实践中,props 是不是这个样子,可以定义默认值,可以定义类型,可以定义是否必须。当调用组件的时候,给props传值。是的,你就当这个组件是一个类,props 是就是成员变量,那 $emit 是什么呢?$emit 是钩子,是事件,是回调,我还没有找到一个很合适的词来形容。这个怎么理解呢?我们就举一个上传图片的例子来讲。
/**
* uploader.vue
* 上传图片组件
*/
<template>
<input type="file" @change="uploadImage" />
template>
<script>
export default {
methods: {
// 上传方法
uploadImage() {
// 开始上传事件
...
this.$emit('start')
// 上传成功事件
...
this.$emit('success', data)
// 上传失败事件
...
this.$emit('failed', error)
}
}
}
script>
/**
* 调用组件
*/
<template>
<uploader @start="uploadStart" @success="uploadSuccess" @failed="uploadFailed" />
template>
<script>
export default {
methods: {
// 开始上传事件
uploadStart(){},
// 上传成功事件
uploadSuccess(data){},
// 上传失败事件
uploadFailed(error){}
}
}
script>
就像上面这段伪代码一样,你在组件里定义一个 $emit('foo'),在调用组件的时候,就 @foo="funForFoo"。$emit 组件里某个事件发生的时候,将会通知外面(父组件),父组件接收事件进行处理。就是这样,没有很麻烦。当然,组件不止这些,但其他的部分还比较好理解,这两个好好理解了,你再去看看别人写的各种轮子,其实没啥,你也可以。再就是路由router和数据可持久vuex这些,路由没啥好说的,你就记住是拿h5的history的state那几接口来实现的。而数据持久化,vuex,我曾在我的群里给小伙伴们说过,就是类的思想嘛,state 成员变量,mutations 是 set 函数,getters 是 get 函数,actions 是调用 set 函数的其他函数,是吧,好理解吧。如果你有通读完 vuex 的文档,你会发现我上面举的类的例子来解释 vuex,非常的相似,就是那么回事儿,找到这个关系了,前端数据持久化,你自己也可以搞。接着说一下请求,这个呢,跟 vue.js 没关系,它就是请求,你用 xml http request 行不行?可以,你用 axios 行不行?可以,你用 fetch 行不行?可以,你用 jq ajax 也可以,它不存在跟 vue.js 有什么绑定关系,不要迷糊。最后甩一段代码,当 Vue 单页应用,一个页面请求未完全结束就被调走到其他路由的时候,需要把前一个页面的未完成的请求,全部干掉,不影响当前路由的请求,axios cancel token 配置。
/** * 关于 axios cancel token 的配置 * 当 Vue 单页应用,一个页面请求未完全结束就被调走到其他路由的时候 * 需要把前一个页面的未完成的请求,全部干掉,不影响当前路由的请求 */import Vue from 'vue'import App from './App.vue'import router from './router'// Axios configurationwindow.axios = require('axios')// ... other config// Axios request interceptors// 定义 cancel tokenlet requestSource = { token: null, cancel: null }const CancelToken = window.axios.CancelToken// 打断 axios 请求,当 config.cancelToken 有值的时候,请求会被 cancel 掉window.axios.interceptors.request.use(config => { config.cancelToken = requestSource.token return config}, error => { return Promise.reject(error)})// Router Before Each Configuration// 在进入每个新路由之前,给 axios 的 cancel token 赋值router.beforeEach((to, from, next) => { requestSource.cancel && requestSource.cancel() requestSource = CancelToken.source() next()})new Vue({ router, render: h => h(App)}).$mount('#app')
我再甩一段,更好的辅助你理解上面说的组件 props 和 $emit。是一段 gist, 我比较懒,懒得封成轮子,下面的是单文件上传。当然还有多文件上传,有需要可以关注我,找我要。下面这段是封的基于 ElementUI,单次-单文件-分片-直传-阿里云OSS的组件。
:class="'w-' + size + ' h-' + size"
class="upload-card hover:border-blue-300 hover:shadow"
action=""
name="file"
accept="images/*"
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="onUpload"
:on-progress="onProgress"
:on-success="onSuccess"
:on-error="onError"
>
"circle" :percentage="percent" v-if="uploading">
"contain" :src="img ? oss + img : image" v-else-if="image || img">
class="'w-' + size + ' h-' + size" class="upload-plus el-icon-plus" v-else></i>
export default {
name: "ImageUpload",
data() {
return {
oss: process.env.MIX_ALIYUN_OSS_URL,
uploading: false,
percent: 0,
object: null,
image: null,
};
},
props: {
size: {
type: Number,
default: 32
},
path: {
type: String,
default: 'image'
},
img: {
type: String,
default: null,
}
},
methods: {
beforeUpload(file) {
const isIMG = file.type.substr(0, 6) === "image/";
const isLt4M = file.size / 1024 / 1024 < 4;
if (!isIMG) {
this.$message.error("上传只能是图片格式!");
}
if (!isLt4M) {
this.$message.error("上传大小不能超过 4MB!");
}
this.uploading = true;
return isIMG && isLt4M;
},
async onUpload(option) {
let file = option.file;
try {
let object = this.generateObject(file.name);
await ossClient.multipartUpload(object, file, {
progress: (p, checkpoint) => {
option.file.percent = Math.floor(p * 100);
option.onProgress(option.file);
}
});
option.file.response = { object: object };
return option.file.response;
} catch (err) {
option.file.err = err;
return option.file.err;
}
},
generateObject(fileName) {
let i = fileName.lastIndexOf(".");
let suffix = fileName.substr(i + 1);
let name = _.generateKey(16);
return `/${this.path}/${name}.${suffix}`;
},
onProgress(event, file, fileList) {
this.percent = event.percent;
},
onSuccess(response, file, fileList) {
this.object = response.object;
this.image = URL.createObjectURL(file.raw);
this.uploading = false;
this.$message.success(`${file.name} 上传成功`);
this.$emit("uploadSuccess", this.object);
},
onError(err, file, fileList) {
this.image = null;
this.object = null;
this.uploading = false;
this.$message.error(`${file.name} 上传失败`);
this.$emit("uploadError");
}
}
};
>
.upload-card {
@apply flex items-center justify-center overflow-hidden rounded-lg bg-white cursor-pointer border border-dashed border-gray-300;
}
.upload-plus {
@apply flex items-center justify-center text-4xl text-gray-500;
}
.upload-card .el-upload {
display: flex !important;
align-items: center !important;
justify-content: center !important;
}