Vue从0基础到大神学习完整教程完整教程(附代码资料)主要内容讲述:vue基本概念,vue-cli的使用,vue的插值表达式,{{ gaga }},{{ if (obj.age > 18 ) { } }},vue指令,综合案例 - 文章标题编辑vue介绍,开发vue的方式,基本使用,如何覆盖webpack配置,目录分析与清理,vue单文件组件的说明,vue通过data提供数据,通过插值表达式显示数据,安装vue开发者工具,v-bind指令,v-on指令,v-if 和 v-show,v-model,v-text 和 v-html。day-08vuex介绍,语法,模块化,小结。面经PC端-element (上)初始化,request,router,login模块,layout模块,dashboard模块(了解)。面经PC端 - Element (下)Article / list 列表,Article / add 添加,Article / del 删除,Article / upd 修改,Article / preview 预览,yarn-补充。vue指令(下),成绩案例,计算属性,属性监听v-for,样式处理,基本结构与样式,基本渲染,删除,新增,处理日期格式,基本使用,计算属性的缓存的问题,成绩案例-计算属性处理总分 和 平均分,计算属性的完整写法,大小选,基本使用,复杂类型的监听-监听的完整写法,成绩案例-监听数据进行缓存,配置步骤 (两步),使用演示。vue指令(下),成绩案例,计算属性,属性监听v-for,样式处理,基本结构与样式,基本渲染,删除,新增,处理日期格式,基本使用,计算属性的缓存的问题,成绩案例-计算属性处理总分 和 平均分,计算属性的完整写法,大小选,基本使用,复杂类型的监听-监听的完整写法,成绩案例-监听数据进行缓存,配置步骤 (两步),使用演示。组件化开发,组件通信,todo案例,作业什么是组件化开发,组件的注册,全局注册组件,组件的样式冲突 ,组件通信 - 父传子 props 传值,v-for 遍历展示组件练习,单向数据流,组件通信 - 子传父,props 校验,布局,任务组件todo,列表,删除,修改:不做了!下面代码其实就是我想让大家练习,添加,剩余数量,清空已完成,小选与大选,筛选:作业,本地存储,附加练习_1.喜欢小狗狗吗,附加练习_2.点击文字变色,附加练习_3. 循环展示狗狗,附加练习_4.选择喜欢的狗。v-model,ref 和 nextTick,dynamic 动态组件,自定义指令,插槽,案例:商品列表v-model 语法糖,v-model给组件使用,动态组件的基本使用,自定义指令说明,自定义指令 - 局部注册,自定义指令 - 全局注册,自定义指令 - 指令的值,默认插槽 slot,后备内容 (默认值),具名插槽,作用域插槽,案例概览,静态结构,MyTag 组件,MyTable 组件。生命周期,单页应用程序与路由,vue-router研究生命周期的意义,生命周期函数(钩子函数),组件生命周期分类,SPA - 单页应用程序,路由介绍,vue-router介绍,vue-router使用,配置路由规则,路由的封装,vue路由 - 声明式(a标签转跳)导航,vue路由 - 重定向和模式,vue路由 - 编程式(JS代码进行转跳)导航,综合练习 - 面经基础版,组件缓存 keep-alive。面经 H5 端 - Vant(上)初始化,vant,axios封装,router,主题定制-了解,登录&注册。面经 H5 端 - Vant(下)列表,详情,收藏 与 喜欢,我的(个人中心)。Day01_vuex今日学习目标(边讲边练),1.vuex介绍,2.vuex学习内容,3.vuex例子准备,vuex-store准备,5.vuex-state数据源,【vuex-mutations定义-同步修改,【vuex-mutations使用,8.vuex-actions定义-异步修改,9.vuex-actions使用,10.vuex-重构购物车-准备Store,11.vuex-重构购物车-配置项(上午结束),vuex-getters定义-计算属性,13.vuex-getters使用,14.vuex-modules定义-分模块,15.分模块-影响state取值方式,16.分模块-命名空间,扩展: 使用Devtools调试vuex数据。
全套笔记资料代码移步: 前往gitee仓库查看
感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~
面经 H5 端 - Vant(上)
接口文档地址:
初始化
新建项目
创建项目
vue create hm-vant-h5
- 选项
Vue CLI v5.0.4
? Please pick a preset: (Use arrow keys)Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint)
> Manually select features 选自定义
- 手动选择功能
- 选择vue的版本
3.x
> 2.x
- 是否使用history模式
- 选择css预处理
- 选择eslint的风格
- 选择校验的时机 (直接回车)
- 选择配置文件的生成方式 (直接回车)
- 是否保存预设,下次直接使用? => 不保存,输入 N
- 等待安装,项目初始化完成
- 启动项目
npm run serve
ESlint
代码规范:一套写代码的约定规则。例如:赋值符号的左右是否需要空格?一句结束是否是要加;?...
没有规矩不成方圆
ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。在创建项目时,我们使用的是 [JavaScript Standard Style]( 代码风格的规则。
规范说明
建议把: 看一遍,然后在写的时候, 遇到错误就查询解决。
下面是这份规则中的一小部分:
- 字符串使用单引号 – 需要转义的地方除外
- 无分号 – [这](
- 关键字后加空格
if (condition) { ... }
- 函数名后加空格
function name (arg) { ... }
- 坚持使用全等
===
摒弃==
一但在需要检查null || undefined
时可以使用obj == null
- ......
示范错误
如果你的代码不符合standard的要求,eslint会跳出来刀子嘴,豆腐心地提示你。
下面我们在main.js中随意做一些改动:添加一些空行,空格。
import Vue from 'vue'
import App from './App.vue'import './styles/index.less'
import router from './router'
Vue.config.productionTip = falsenew Vue ( {render: h => h(App),router
}).$mount('#app')
按下保存代码之后:
你将会看在控制台中输出如下错误:
eslint 是来帮助你的。心态要好,有错,就改。
修改
- 手动修正
根据错误提示来一项一项手动修正。
如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码(func-call-spacing, space-in-parens,.....)去 ESLint 规则列表中查找其具体含义。
打开 [ESLint 规则表]( + F)这个代码,查找对该规则的一个释义。
- 通过vscode中的eslint插件来实现自动修正
- eslint会自动高亮错误显示
- 通过配置,eslint会自动帮助我们修复错误
- 如何安装
- 如何配置 加vsc编辑器
// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {"source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false
- 注意:
- eslint的配置文件必须在根目录下,这个插件才能才能生效。打开项目必须以根目录打开,一次打开一个项目
- **使用了eslint校验之后,把vscode带的那些格式化工具全禁用了 **
- **Beatify **
- Prettier - Code formatter
我的完整配置,供参考
{"editor.tabSize": 2,"workbench.iconTheme": "vscode-icons","explorer.compactFolders": false,"security.workspace.trust.untrustedFiles": "open","git.enableSmartCommit": true,// 不要有分号"prettier.semi": false,// 使用单引号"prettier.singleQuote": true,// 默认使用prittier作为格式化工具"editor.defaultFormatter": "esbenp.prettier-vscode","emmet.showAbbreviationSuggestions": true,"emmet.includeLanguages": {"javascript": "javascriptreact"},"editor.suggest.snippetsPreventQuickSuggestions": false,"editor.fontSize": 16, // 设置文字的大小"window.zoomLevel": 1,"emmet.triggerExpansionOnTab": true,// 保存时自动格式化 - 按照eslint的规则格式化"editor.codeActionsOnSave": {"source.fixAll": true},// 保存代码,不自动格式化"editor.formatOnSave": false
}
目录结构
强烈建议大家严格按照老师的步骤进行调整,为了符合企业规范
为了更好的实现后面的操作,我们把整体的目录结构做一些调整。
目标:
- 删除初始化的一些默认文件
- 修改没删除的文件
- 新增我们需要的目录结构
index.html
public/index.html
修改视口,开发的是移动端项目
<metaname="viewport"content="width=device-width,initial-scale=1.0, user-scalable=0"
/>
main.js
不需要修改
router/index.js
删除默认的路由配置
import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [
]const router = new VueRouter({routes
})export default router
App.vue
样式删除
<template><div id="app"><router-view/></div>
</template>
删除文件
- src/views/AboutView.vue
- src/views/HomeView.vue
- src/components/HelloWorld.vue
- src/assets/logo.png
新增目录
- src/api 目录
- 存储接口模块 (发送ajax请求接口的模块)
- src/utils 目录
-
存储一些工具模块 (自己封装的方法)
-
src/assets 目录
- 新增项目使用的素材
目录效果如下:
vant
组件库:第三方封装好了很多很多的组件,整合到一起就是一个组件库。
组件库并不是唯一的
pc: element-ui iview ant-design
移动:vant-ui
- 安装vant-ui
npm i vant@latest-v2npm i vant@latest-v2 -S --legacy-peer-deps
-
全部导入-了解
-
在main.js中
import Vant from 'vant';
import 'vant/lib/index.css';
// 把vant中所有的组件都导入了
Vue.use(Vant)
- 即可使用
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
==vant-ui提供了很多的组件,全部导入,会导致项目打包变得很大。==
按需加载
- 安装一个插件
npm i babel-plugin-import -Dnpm i babel-plugin-import --force
- 在
babel.config.js
中配置
module.exports = {presets: ['@vue/cli-plugin-babel/preset'],plugins: [['import', {libraryName: 'vant',libraryDirectory: 'es',style: true}, 'vant']]
}
- 按需加载,在
main.js
import { Button, Icon } from 'vant'Vue.use(Button)
Vue.use(Icon)
单独配置
- main.js引入,后期组件会越来越多,提取到
utils/vant.js
中
import Vue from 'vue'
import { Button, Icon } from 'vant'
Vue.use(Button)
Vue.use(Icon)
- main.js中引入
import '@/utils/vant'
app.vue
中进行测试
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
- 后续我们用到的所有的组件:
import Vue from 'vue'
import {Button, Icon, Tabbar, TabbarItem,NavBar,Form,Field,Toast,Cell,List,Grid,GridItem,CellGroup
} from 'vant'Vue.use(Button)
Vue.use(Icon)
Vue.use(Tabbar)
Vue.use(TabbarItem)
Vue.use(NavBar)
Vue.use(Form)
Vue.use(Field)
Vue.use(Toast)
Vue.use(Cell)
Vue.use(List)Vue.use(Grid)
Vue.use(GridItem)
Vue.use(CellGroup)
等比适配【重要】
官方说明:
npm i postcss-px-to-viewport@1.1.1 -D --force
- 项目根目录, 新建postcss的配置文件
postcss.config.js
// postcss.config.js
module.exports = {plugins: {'postcss-px-to-viewport': {viewportWidth: 375,},},
};// 重新启动项目!
- 如何写页面的样式,比如 375屏幕下,写200px大小的盒子,直接写就可以
- webpack装的这个包,会自动帮我们转换为vw vh单位!
axios封装
接口文档地址:
我们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址等)
一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用
- 安装 axios
npm i axios -S
- 新建
utils/request.js
封装 axios 模块
利用 axios.create 创建一个自定义的 axios 来使用
/* 封装axios用于发送请求 */
import axios from 'axios'// 创建一个新的axios实例
const request = axios.create({baseURL: 'timeout: 5000 // 超时5000ms
})// 添加请求拦截器
request.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config
}, function (error) {// 对请求错误做些什么return Promise.reject(error)
})// 添加响应拦截器
request.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {// 对响应错误做点什么return Promise.reject(error)
})export default request
- 测试 (可以先注册后登录)
<script>
export default {async created () {// const res = await request.post('/user/register', {// username: 'shuaipeng',// password: '123456'// })const res = await request.post('/user/login', {username: 'shuaipeng',password: '123456'})console.log(res)}
}
</script>
router
但凡是单个页面,独立展示的,都是一级路由
路由设计:基于昨天面试经验
- 登录页 (一级) Login
- 注册页(一级) Register
- 文章详情页(一级) Detail
- 首页(一级) Layout
- 面经(二级)Article
- 收藏(二级)Collect
- 喜欢(二级)Like
- 我的(二级)My
一级
router/index.js
配置一级路由, 一级views组件于教学资料中直接 CV 即可
import Vue from 'vue'
import VueRouter from 'vue-router'import Login from '@/views/Login'
import Register from '@/views/Register'
import Detail from '@/views/Detail'
import Layout from '@/views/Layout'
Vue.use(VueRouter)const router = new VueRouter({routes: [{ path: '/login', component: Login },{ path: '/register', component: Register },{ path: '/article/:id', component: Detail },{path: '/',component: Layout}]
})export default router
清理 App.vue
<template><div id="app"><router-view/></div>
</template><script>
export default {async created () {}
}
</script>
- 底部 tabbar 标签页
utils/vant.js
引入组件
import { Button, Icon, Tabbar, TabbarItem } from 'vant'
Vue.use(Tabbar)
Vue.use(TabbarItem)
Layout.vue
<template><div class="layout-page">首页架子 - 内容区域<van-tabbar><van-tabbar-item icon="notes-o">面经</van-tabbar-item><van-tabbar-item icon="star-o">收藏</van-tabbar-item><van-tabbar-item icon="like-o">喜欢</van-tabbar-item><van-tabbar-item icon="user-o">我的</van-tabbar-item></van-tabbar></div>
</template>
二级
router/index.js
配置二级路由, 二级路由views组件于教学资料中直接 CV 即可
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/Login'
import Register from '@/views/Register'
import Detail from '@/views/Detail'
import Layout from '@/views/Layout'import Like from '@/views/Layout/Like'
import Article from '@/views/Layout/Article'
import Collect from '@/views/Layout/Collect'
import User from '@/views/Layout/User'Vue.use(VueRouter)const router = new VueRouter({routes: [{ path: '/login', component: Login },{ path: '/register', component: Register },{ path: '/article/:id', component: Detail },{path: '/',component: Layout,children: [{ path: '/', redirect: '/article' },{ path: '/article', component: Article },{ path: '/like', component: Like },{ path: '/collect', component: Collect },{ path: '/user', component: User }]}]
})export default router
layout.vue
配置路由出口, 配置 tabbar
<template><div class="layout-page"><router-view></router-view><van-tabbar route><van-tabbar-item to="/article" icon="notes-o">面经</van-tabbar-item><van-tabbar-item to="/collect" icon="star-o">收藏</van-tabbar-item><van-tabbar-item to="/like" icon="like-o">喜欢</van-tabbar-item><van-tabbar-item to="/user" icon="user-o">我的</van-tabbar-item></van-tabbar></div>
</template><style lang="less" scoped>
.layout-page {.van-tabbar-item--active {color: #FA6D1D;}
}
</style>
效果图:
主题定制-了解
整体网站风格,其实都是橙色的,可以通过变量覆盖的方式,制定主题色
babel.config.js
制定样式路径
module.exports = {presets: ['@vue/cli-plugin-babel/preset'],plugins: [['import', {libraryName: 'vant',libraryDirectory: 'es',// 指定样式路径style: (name) => `${name}/style/less`}, 'vant']]
}
vue.config.js
覆盖变量
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,css: {loaderOptions: {less: {lessOptions: {modifyVars: {// 直接覆盖变量'blue': '#FA6D1D',},},},},},
})
登录&注册
login布局
使用组件
- van-nav-bar
- van-form
- van-field
- van-button
vant-ui.js
注册
import Vue from 'vue'
import {NavBar,Form,Field
} from 'vant'
Vue.use(NavBar)
Vue.use(Form)
Vue.use(Field)
Login.vue
使用
<template><div class="login-page"><van-nav-bar title="面经登录" /><van-form @submit="onSubmit"><van-fieldv-model="username"name="username"label="用户名"placeholder="用户名":rules="[{ required: true, message: '请填写用户名' }]"/><van-fieldv-model="password"type="password"name="password"label="密码"placeholder="密码":rules="[{ required: true, message: '请填写密码' }]"/><div style="margin: 16px"><van-button block type="info" native-type="submit">提交</van-button></div></van-form></div>
</template><script>
export default {name: 'login-page',data () {return {username: 'zhousg',password: '123456'}},methods: {onSubmit (values) {console.log('submit', values)}}
}
</script>
Login.vue
添加 router-link 标签(跳转到注册)
<template><div class="login-page"><van-nav-bar title="面经登录" /><van-form @submit="onSubmit">...</van-form><router-link class="link" to="/register">注册账号</router-link></div>
</template>
Login.vue
调整样式
<style lang="less" scoped>
.link {color: #069;font-size: 12px;padding-right: 20px;float: right;
}
</style>
register布局
register.vue
<template><div class="login-page"><van-nav-bar title="面经注册" /><van-form @submit="onSubmit"><van-fieldv-model="username"name="username"label="用户名"placeholder="用户名":rules="[{ required: true, message: '请填写用户名' }]"/><van-fieldv-model="password"type="password"name="password"label="密码"placeholder="密码":rules="[{ required: true, message: '请填写密码' }]"/><div style="margin: 16px"><van-button block type="primary" native-type="submit">注册</van-button></div></van-form><router-link class="link" to="/login">有账号,去登录</router-link></div>
</template><script>
export default {name: 'register-page',data () {return {username: 'zhousg',password: '123456'}},methods: {onSubmit (values) {console.log('submit', values)}}
}
</script><style lang="less" scoped>
.link {color: #069;font-size: 12px;padding-right: 20px;float: right;
}
</style>
toast 轻提示
1/组件内 **通过this直接调用
import { Toast } from 'vant';
Vue.use(Toast) // vant.js内部注册this.$toast('提示内容') // 全局使用
2/某个组件内,单独导入,单独调用
import { Toast } from 'vant';
Toast('提示内容');
实现注册
- 点击按钮实现注册:有成功和错误的报错!
async onSubmit (values) {try {await this.$axios({ method: 'POST', url: '/user/register', data: values })this.$toast.success('注册成功')} catch (error) {this.$toast.fail(error.response.data.message)}
}
- 成功注册后:转跳到登录的路由
this.$router.push({path:"/login"})
实现登录
- 点击按钮实现登录的api
async onSubmit (values) {try {const { data } = await this.$axios({ method: 'POST', url: '/user/login', data: values })this.$toast.success('登录成功')localStorage.setItem('h5-token', 'Bearer ' + data.token) // 存token,后续所有接口都要用!this.$router.push({ path: '/article' }) // 路由切换主页layout下Arcticle路由} catch (error) {this.$toast.fail(error.response.data.message)}
}
统一错误处理
request.js
响应拦截器,统一处理错误提示
import { Toast } from 'vant'// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {if (error.response) {// 有错误响应, 提示错误提示Toast(error.response.data.message)}// 对响应错误做点什么return Promise.reject(error)
})
路由拦截
- token去限制
这个 面经移动端 项目,只对 登录用户 开放,如果未登录,一律拦截到登录
-
如果访问的是首页,无token, 拦走
-
如果访问的是列表页,无token,拦走
-
如果访问的是详情页,无token,拦走
....
分析:哪些页面,是不需要登录,就可以访问的! => 注册 和 登录 (白名单)
核心逻辑:
- 判断用户有没有token, 有token, 直接放行
- 没有token,
- 如果是白名单中的页面,直接放行
- 否则,无token,且在访问需要权限访问的页面,直接拦截到登录
vue路由内置了一个语法
**全局前置守卫: 路由守卫 **
- 所有的路由一旦被匹配到,在真正渲染解析之前,都会先经过全局前置守卫
- 只有全局前置守卫放行,才能看到真正的页面
// 全局前置守卫:
// 1. 所有的路由一旦被匹配到,在真正渲染解析之前,都会先经过全局前置守卫
// 2. 只有全局前置守卫放行,才能看到真正的页面// 任何路由,被解析访问前,都会先执行这个回调
// 1. from 你从哪里来, 从哪来的路由信息对象
// 2. to 你往哪里去, 到哪去的路由信息对象
// 3. next() 是否放行,如果next()调用,就是放行 => 放你去想去的页面
// next(路径) 拦截到某个路径页面const whiteList = ['/login', '/register'] // 白名单列表,记录无需权限访问的所有页面router.beforeEach((to, from, next) => {const token = localStorage.getItem("h5-token");// 如果有token,直接放行if (token) {next()} else {// 没有token的人, 看看你要去哪// (1) 访问的是无需授权的页面(白名单),也是放行// 就是判断,访问的地址,是否在白名单数组中存在 includesif (whiteList.includes(to.path)) {next()} else {// (2) 否则拦截到登录next('/login')}}
})