版本号 | 作者 | 备注 | |
---|---|---|---|
v1.0.1 | 飞豺 | 8416837 | Vue 2.6.10 |
网友:"到今天这个时代有些人学完了js、html5/dom/bom直接跳过jQ去学vue我觉得完全没问题,所以不懂jQ的人会vue当然可以,本来前端的基础就是js而不是jQ。而且vue本身走的就是模块化的开发方式,就是让项目更好维护,更好升级,在这些方面绝对比jQ更优秀。jQuery之所以被替代,一是开发方式保守、老旧、效率低,二是因为html5出了很多新的api,完全可以代替jQ,就连bootstrap重构都已经申明将剔除jQ。退一万步说,前端最基础的还是js,只要你js技术过关,不管是学jQ还是vue都会很快。"
要编写可维护的前端代码绝非易事。我们已经用MVC模式通过koa实现了后端数据、模板页面和控制器的分离,但是,对于前端来说,还不够。
这里有童鞋会问,不是讲Node后端开发吗?怎么又回到前端开发了?
对于一个全栈开发工程师来说,懂前端才会开发出更好的后端程序(不懂前端的后端工程师会设计出非常难用的API),懂后端才会开发出更好的前端程序。程序设计的基本思想在前后端都是通用的,两者并无本质的区别。这和“不想当厨子的裁缝不是好司机”是一个道理。
改变JavaScript对象的状态,会导致DOM结构作出对应的变化!这让我们的关注点从如何操作DOM变成了如何更新JavaScript对象的状态,而操作JavaScript对象比DOM简单
多了!
这就是MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把开发者从操作DOM的繁琐步骤中解脱出来
! ——廖雪峰
ps:jQuery MVVM框架如JSViews
<template>
<div id="app">
<p>就是一张da图片p>
[外链图片转存失败(img-dlDOl0R2-1562114250786)(https://mp.csdn.net/mdeditor/assets/logo.png)]
<router-view/>
div>
template>
router/index.js定义了简单的路由信息
package.json文件配置:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon -e js,graphql -x node -r dotenv/config ./src/index.js",
"debug": "nodemon -e js,graphql -x node --inspect -r dotenv/config ./src/index.js",
"lint": "eslint --ext .js src"
},
比如,执行yarn start
则走start
脚本
<div id="app">
<app-nav>app-nav>
<app-view>
<app-sidebar>app-sidebar>
<app-content>app-content>
app-view>
div>
// 演变步骤
render: function (createElement) {
return createElement(App);
}
render (createElement) {
return createElement(App);
}
render (h){
return h(App);
}
It comes from the term “hyperscript”, which is commonly used in many virtual-dom implementations. “Hyperscript” itself stands for “script that generates HTML structures” because HTML is the acronym for “hyper-text markup language”. – by 尤雨溪
持有所有被ref定义的组件
组件树
。Vue.component('todo-item', {
template: '这是个待办项 '
})
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
参数带上这个,参数会被对象化,如果参数是数字或字符串,会被拆分成一个个的数字或字符;
// 假如有三个接口调用动作a1,a2,p5
Promise.all([a1,a2,p5]).then((result) => {
console.log(result) // 结果数组或者错误
}).catch((error) => {
console.log(error)
})
const storeLogin = new Vuex.Store({
state: {
// 存储token
Authorization: localStorage.getItem('Authorization') ? localStorage.getItem('Authorization') : ''
},
mutations: {
// 修改token,并将token存入localStorage
changeLogin(state,user) {
console.log('进入changeLogin')
state.Authorization = user.Authorization;
localStorage.setItem('Authorization', user.Authorization);
}
}
});
又如↓
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
mutations下的函数只适合接收一个对象参数,state是默认传入,不能把state当做形参
先在html引入rules
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
data里定义rules
data() {
loginRules: {
// 验证器 validateUsername 是一种特殊的函数
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
},
验证器也在data里面
const validateUsername = (rule, value, callback) => {
// validUsername 是引入的js函数
if (!validUsername(value)) {
callback(new Error('请输入正确的用户名'))
} else {
callback()
}
}
引入validUsername
import { validUsername } from '@/utils/validate' // 导入js
// 引入ui
import ElementUI from 'element-ui' //element-ui的全部组件
import 'element-ui/lib/theme-chalk/index.css'//element-ui的css
Vue.use(ElementUI) //使用elementUI
cnpm install [email protected] -S
<div style="text-align: left">
<el-transfer
v-model="value4"
style="text-align: left; display: inline-block"
filterable
:render-content="renderFunc"
:titles="['用户角色', '已选角色']"
:button-texts="['放弃', '选择']"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}'
}"
:data="roleData"
:props="defaultProps"
@change="handleChange"
>
<el-button slot="left-footer" class="transfer-footer" size="small">操作el-button>
<el-button slot="right-footer" class="transfer-footer" size="small">操作el-button>
el-transfer>
div>
data() {
return {
// 角色数据
data: [],
// 别名
defaultProps: {
key: 'roleId',
label: 'roleName',
disabled: false
},
// 不要value4无法移动元素
value4: [1],
renderFunc(h, option) {
// 生成元素的显示名称
return <span>{ option.roleName }</span>
// return { option.roleId } - { option.roleName }
},
cc.vue
# 新建cc组件
template>
<div>
中国工农红军
<ul>
<li v-for="site in sites" :key="site.name">
{{site.url}}
<a :href="site.url" target="_blank">{{site.name}}a>
li>
ul>
<input type="button" value="点击我" @click="printText"/>
div>
template>
<script>
// import { METHODS } from 'http'
export default {
name: 'Cc',
methods: {
clickTest: function () {
alert('你点击了按钮')
},
printText: function () {
console.log('你点击了按钮')
}
},
data () {
return {
msg: '书籍是人类进步的阶梯',
msg2: 'Apple',
sites: [
{url: 'http://router.vuejs.org/', name: 'Jack'},
{url: 'http://vuex.vuejs.org/', name: 'Tom'},
{url: 'https://github.com/vuejs/awesome-vue', name: 'Jimy'}
]
}
}
}
script>
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
style>
index.js
# 注入组件
import cc from '@/components/cc'
因为导入的函数没加括弧
// 函数需要括弧,↓对的
menuTree().then(response => {
}
// 组件内部增加下述代码 - 局部请求头
import Axios from 'axios'
Axios.defaults.headers.post['Content-Type'] = 'application/json'
# 或者在axios api里加入
export function call(data) {
return request({
headers: {
'Content-Type': 'application/json' // 设置请求头请求格式为JSON
},
url: url,
method: 'post',
data
})
}
vue-cli-service serve --mode development # 检查有无安装vue-cli-service serve
检查命令执行后,报错:
'vue-cli-service' 不是内部或外部命令,也不是可运行的程序
解决办法:将原node_modules
重命名,重新执行cnpm run dev
即可。因此node_modules
最好是每个项目有一个个性化的,比如某些项目某些模块必须npm
安装。
npm install -g increase-memory-limit
# 进入项目文件夹运行:
increase-memory-limit
// cmpnt = () => import(`@/views${m_url}`)
cmpnt = (resolve) => require([`@/views${m_url}`], resolve)
// 上面的改成下面的
查看原理
# install axios
cnpm install axios --save-dev
# --save-dev以省掉手动修改package.json文件的步骤
axios发送ajax请求
<script src="/js/axios.min.js"></script>
window.onload=function(){
new Vue({
el:'#app',
data:{
users:{
name:'',
age:''
}
},
methods:{
sendPsot(){
axios.post('post.php', {
name: this.users.name,
age: this.users.age,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
});
}
config/index.js
,proxyTable里增加内容(老式
)proxyTable: {
// 解决跨域
'/tbapi':{
// target: "http://api.douban.com/v2",
target: "https://suggest.taobao.com",
changeOrigin:true,
pathRewrite:{
'^/tbapi':''
}
}
},
组件js
// 跨域
Axios.defaults.baseURL = '/tbapi'
Axios.defaults.headers.post['Content-Type'] = 'application/json'
mounted() {
//GET
this.$ajax({
method: 'get',
// tbapi会代替localhost
url: '/sug?code=utf-8&q=电冰箱',
// url: '/sug?code=utf-8&q=电冰箱&callback=cb',
}).then(response => {
// response包含config data等
var resData = response.data.result
iceBoxes = resData
resData.forEach(item => {
console.log(item[0])
console.log(item[1])
// console.log('数据序号'+i+'=='+item)
})
}).catch(function (err) {
console.log(err)
})
//POST
this.$ajax({
method: 'post',
url: '/sug?code=utf-8&q=iPhone',
// data: {
// code: 'utf-8',
// q: 'iPhone'
// }
}).then(response => {
// response包含config data等
var resData = response.data.result
resData.forEach(item => {
console.log(item[0])
console.log(item[1])
// console.log('数据序号'+i+'=='+item)
})
}).catch(function (err) {
console.log(err)
});
}
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
// 也就是说,全局store对象拥有原生的dispatch方法,用于请求API
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}
上文的$store来自这里@/store/index.js
,片段↓
const store = new Vuex.Store({
modules,
getters
})
// 导出实例this的store属性
export default store
store的dispatch,该单词是发送的意思
axios的配置
// request即service-axios
import request from '@/utils/request'
// ↑request含有拦截器,url改为合适的baseUrl
export function login(data) {
console.log('axios实例==',request)
return request({
url: '/user/login',
method: 'post',
data
})
}
当index.vue里的store.dispatch执行请求时,即会找到上面的login函数,由login函数发出调用请求,接着,我们看request.js
里的代码
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// global全局配置
import { baseUrl } from '@/utils/global'
// create an axios instance 没错,service就是axios实例,import@/utils/request即注入service-axios实例
const service = axios.create({
baseURL: baseUrl, // url = base url + request url
// baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
↑这是axios的配置,配置了url和超时时间,当执行$store.dispatch时,即会加上baseUrl进行请求。
接收响应↓,也来自request.js
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
// 1 request.js
const service = axios.create({
// baseURL: baseUrl, // url = base url + request url
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// 2 user.js
// params: data
data
// 3 user.js
// method: 'post',
method: 'get',
axios.defaults.withCredentials=true;
import TestMode from './Test' // 导入相对路径的Test.vue
...
components: { TestMode }, // 注册组件
<test-mode v-if="testPageVisible" ref="testMode2" @refreshDataList="getList">test-mode>
test(row) { // 这里是父组件
this.testPageVisible = true
this.$nextTick(() => {
this.$refs.testMode2.test(Object.assign({}, row)) // 调用子组件的函数 testMode2
})
},
methods: {
test() { // 这里是Test.vue组件·················
this.dialogFormVisible = true
}
}
事态紧急跨域用表格代替九宫格,不过不正规。
正规的待续
# 安装es6-promise
npm install es6-promise --save-dev
import Es6Promise from 'es6-promise'
Es6Promise.polyfill();
上文安装的promise
只是针对性的,要彻底兼容IE还需要研究。