b站教学视频 Vue2_项目_黑马头条-移动端项目
视频里配套资料: http://m6z.cn/6guyjV
最新的接口基地址: http://geek.itheima.net/
只记录自己get到的一些知识,详细的老师的配套的笔记里面都有
vue-cli脚手架创建项目
- 安装vue-cli脚手架
npm install -g @vue/cli
- 创建项目 采用自定义方式去创建项目
vue create hmtt
上下箭头切换, 回车确认, 空格选中
? Please pick a preset:
Default ([Vue 2] babel, eslint)
Default (Vue 3 Preview) ([Vue 3] babel, eslint)
> Manually select features
手动选择特性: Babel, Router, Vuex, CSS Pre-processors, Linter
? Please pick a preset: Manually select features
? Check the features needed for your project:
(*) Choose Vue version
(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
(*) Router
(*) Vuex
>(*) CSS Pre-processors
(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
版本Vue2.x
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
> 2.x
3.x (Preview)
路由是否使用history模式:不采用
css 预处理器: 使用less
eslint语法风格:Standard (一定)
检查节点:保存时检查,提交时检查 (提交时可以不选)
存储插件配置位置:单独放在不同的文件中
接下来,它会问你是否要保存前面的设置作为预设方案,以便后续创建其它项目时直接使用。
如果选择Y, 保存, 以后就可以一键完成以上步骤
经过长长的等待,创建完毕, 进入文件夹, 启动项目
ESLint
什么是ESLint?
代码检查工具
为什么要使用ESLint?
规范我们写代码的格式, 看着整洁 / 团队内成员风格统一
ESLint在哪里生效?
webpack开发服务器+ESLint配置检查
规范文档: http://www.verydoc.net/eslint/00003312.html
规范文档2: https://standardjs.com/rules-zhcn.html
规范文档3: http://eslint.cn/docs/rules/
vscode的eslint插件
ctrl+s 这个插件就会修复常见的eslint抛出的错误
- 下载这个插件到vscode中
注意: 一定要把脚手架工程, 作为vscode根目录, 因为eslint要使用配置文件.eslintrc
- 一定要配置插件监测的时机, 修改ESLint插件配置
不用管别的, 把红框的放在{}内即可
"eslint.run": "onType",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
更多的规则可以参考这里: https://www.cnblogs.com/jiaoshou/p/12218642.html
.eslintrc.js 配置文件关闭驼峰命名
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// 取消文件和变量的驼峰命名
'vue/multi-word-component-names': 0,
camelcase: 'off'
}
vant组件库
移动端组件库
vant官网:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/quickstart
- 下载vant组件库
yarn add vant
# Vue 2 项目,安装 Vant 2:
npm i vant@latest-v2 -S
- 下载插件
yarn add babel-plugin-import -D
- 在babel.config.js-添加如下配置
module.exports = {
// ...省略了其他
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
};
一定要重启vscode和webpack开发服务器才会生效
组件库样式的定制
vant组件配置 - less文件
- src/styles/cover.less - vant定制less变量统一在这管理
// NavBar导航
@nav-bar-background-color:#007bff;
@nav-bar-title-text-color:white;
- vue.config.js - 注释变量, 放开引入文件路径
// 不要手动写绝对路径, 用代码来动态获取, 绝对地址
const path = require('path')
// console.log(__dirname) // 当前文件, 所在文件夹, 的绝对路径
// 盘符:/......../工程名字, 后面自己拼接 src/styles/cover.less
module.exports = {
css: {
loaderOptions: {
less: {
modifyVars: {
// 直接覆盖变量
// 'nav-bar-background-color': '#007bff',
// 'nav-bar-title-text-color': 'white',
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "${path.resolve(__dirname, 'src/styles/cover.less')}";`
}
}
}
}
}
一定要重启webpack开发服务器, 然后观察效果
移动端适配
PC端一般都是1:1用px还原UI设计图, 靠内容撑开高度
移动端一般都是rem单位进行适配
步骤:
- 下载amfe-flexible 根据网页宽度, 设置html的font-size
yarn add amfe-flexible
- 到main.js引入
import "amfe-flexible"
- 下载postcss和postcss-pxtorem@5.1.1
postcss: 后处理css, 编译翻译css代码
postcss-pxtorem: 把css代码里所有px计算转换成rem
yarn add postcss postcss-pxtorem@5.1.1
- 根目录下创建postcss.config.js文件
module.exports = {
plugins: {
'postcss-pxtorem': {
// 能够把所有元素的px单位转成Rem
// rootValue: 转换px的基准值。
// 编码时, 一个元素宽是75px,则换成rem之后就是2rem
rootValue: 37.5,
propList: ['*']
}
}
}
37.5 是如何得来的?
UI移动端设计图宽度375px, 而flexible.js会/10, 设置html的font-size为37.5
一般屏幕的适配方案为 1rem=屏幕宽度的十分之一
总结:
移动端适配选择哪种?
rem + flexible.js
flexible.js作用是什么?
js代码里获取网页宽度 / 10设置html的font-size的值(px单位)
代码里px如何自动转换rem?
postcss和postcss-pxtorem插件
封装axios
- 下载axios
yarn add axios
- request.js
import ajax from 'axios'
import router from '@/router'
import { Notify } from 'vant'
import { getToken } from '@/utils/token'
// 创建axios实例
const axios = ajax.create({
baseURL: 'http://toutiao.itheima.net',
timeout: 20000 // 超时时间为20s
})
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 如果本地有token则携带在请求头中传给后台
// console.log(config)
if (getToken()?.length > 0 && config.headers.Authorization === undefined) {
config.headers.Authorization = `Bearer ${getToken()}`
}
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response
}, function (error) {
// 对响应错误做点什么
/**
* 响应401说明token失效--》跳转到登录页面
*/
if (error.response.status === 401) {
router.push('/login')
Notify({ type: 'danger', message: '登录已过期' })
}
return Promise.reject(error)
})
// 封装axios方法
export default ({ url, method = 'GET', data = {}, params = {}, headers = {} }) => {
return axios({
url,
method,
data,
params,
headers
})
}
补充
axios库中params里的值为null, 会自动忽略此参数和值, 不发给后台
axios库中data里的值为null, 会发给后台
axios可以在响应错误的拦截器中,按之前的配置重新发起请求 axios(error.config)
- token的续签
定义刷新token的接口方法
// 用户 - 更新token
export const refreshTokenAPI = () => request({
url: '/v1_0/authorizations',
method: 'PUT',
headers: {
Authorization: `Bearer ${store.state.refresh_token}`
}
})
在响应拦截器401处, 调用重新请求token的接口, 然后同步给vuex和本地
axios.interceptors.response.use(function (response) {
return response
}, async function (error) {
if (error.response.status === 401) { // 身份过期
// token续签方式1:
// store.commit('setToken', '')
// router.push({ path: '/login' })
// token续签方式2: refreshToken(用户无感知)
const res = await refreshTokenAPI()
// 再调用一次未完成的请求啊(用户无感知)
// error.config 就是上一次axios请求的配置对象
// console.dir(error.config)
// 把新的token赋予到下一次axios请求的请求头中
error.config.headers.Authorization = 'Bearer ' + res.data.data.token
// return到await的地方
return axios(error.config) //重点!!!:通过axios重新发起请求
} else {
return Promise.reject(error)
}
})
try和catch
- await用于取代then函数, 等待Promise成功结果提取在原地
- await无法获取Promise失败的结果, 一旦失败Promise错误直接抛出到控制台
可以利用try和catch解决上面的问题
try {
// 可能会报错的代码(例如await)
} catch (err) {
// try里代码报错, 捕捉到这里执行
}
路由
$router和$route区别是?
$router下用于跳转路由
$route是路由信息对象
路由的push和replace方法区别?
- push跳转后, 可以返回
- replace跳转后, 无法返回
什么时候用$route.query 什么时候用$route.params
- $route.params —>动态路由
- $route.query —>问号传参
路由的懒加载
路由懒加载 - 查看文档: https://router.vuejs.org/zh/guide/advanced/lazy-loading.html
component: Login
// 改成这个写法
component: () => import('@/views/Login.vue')
- 路由守卫(类似后端的拦截器)
方法1: 全局前置守卫判断
router.beforeEach((to, from, next) => {
// 有token, 不能去登录页
// 无token, 需要用户"权限"的才需要去登录页
if (store.state.token.length > 0 && to.path === '/login') {
// 证明有token-已经登录了
next(false) // 阻止跳转原地呆着
} else {
next()
}
})
方法2: 路由独享守卫(可以理解为局部的守卫,针对某个路由拦截)
{
path: '/login',
component: () => import(/* webpackChunkName: "Login" */ '@/views/Login'),
beforeEnter (to, from, next) {
if (store.state.token.length > 0) { // vuex里有token(代表登录过, 但是一定要注意过期和主动退出要先清除vuex和本地的token, 让其跳转登录页)
return next(false)
}
next()
}
},
时间处理
dayjs第三方库: https://dayjs.fenxianglu.cn/
- 安装
npm install dayjs --save
- 使用
var dayjs = require('dayjs')
//import dayjs from 'dayjs' // ES 2015
dayjs().format()
utils/date.js
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime' // 到指定时间需要的插件
import 'dayjs/locale/zh' // 集成中文
/**
* .....多久之前
* @param {*} 之前的时间
* @returns 系统时间到之前指定时间的距离值
*/
export const timeAgo = (targetTime) => {
// 格式化时间
dayjs.extend(relativeTime)
dayjs.locale('zh')
var a = dayjs()
var b = dayjs(targetTime)
return a.to(b) // 返回多久之前...
}
扩展-自己写多久之前
relativeTime (val) {
const t = new Date(val)
const diff = Date.now() - t.getTime()
const year = Math.floor(diff / (1000 * 3600 * 24 * 365))
if (year) {
return `${year}年前`
}
const month = Math.floor(diff / (1000 * 3600 * 24 * 30))
if (month) {
return `${month}月前`
}
const day = Math.floor(diff / (1000 * 3600 * 24))
if (day) {
return `${day}天前`
}
const hour = Math.floor(diff / (1000 * 3600))
if (hour) {
return `${hour}小时前`
}
const minute = Math.floor(diff / (1000 * 60))
if (minute) {
return `${minute}分钟前`
} else {
return '刚才'
}
}
自定义指令-封装
- utils/directives.js, 定义全局自定义指令插件
import Vue from 'vue'
// 插件对象(必须有install方法, 才可以注入到Vue.use中)
export default {
install () {
Vue.directive('fofo', {
inserted (el) {//插入dom的时候执行
fn(el)
},
update (el) {//更新dom的时候执行
fn(el)
}
})
}
}
function fn (el) {
if (el.nodeName === 'INPUT' || el.nodeName === 'TEXTAREA') {
// 如果直接是input标签/textarea标签
el.focus()
} else {
// 指令在van-search组件身上, 获取的是组件根标签div, 而input在标签内
const inp = el.querySelector('input')
const textArea = el.querySelector('textarea')
// 如果找到了
if (inp || textArea) {
inp && inp.focus()
textArea && textArea.focus()
} else {
// 本身也不是, 子标签里也没有
console.error('请把v-fofo用在输入框标签上')
}
}
}
- 引入到main.js注册
import diretivesObj from '@/utils/directives'
Vue.use(diretivesObj)
Vue.use相关文档: https://cn.vuejs.org/v2/api/#Vue-use
Vue.use(obj) 其实就是注册,内部会调用install方法
自定义指令的inserted何时执行?
- 当指令所在组件, 第一次插入到真实DOM被调用
图片防盗链
在前端可以通过meta来设置referrer policy(来源策略),referrer设置成no-referrer
,发送请求不会带上referrer信息,对方服务器也就无法拦截了
<!-- 解决图片403防盗链问题 -->
<meta name="referrer" content="no-referrer" />
但是如果他们做了其他判断, 我们依旧拿不到此图片
给组件加原生事件
组件默认没有click事件的时候,如果想用click事件怎么办?利用navive
<article-item
v-for="obj in articleList"
:key="obj.art_id"
:obj="obj"
@click.native="$router.push(`/article_detail?aid=${obj.art_id}`)"
:showX="false"
></article-item>
阻止事件冒泡
通过.stop来阻止
<!-- 反馈按钮 -->
<van-icon name="cross" @click.stop="onCloseClick" />
防抖和节流
后面再找点资料详细看看
数组去重
利用 Set 和 Array.from实现
this.history = Array.from(new Set(this.history)) // 去重
文件上传-隐藏域
当点击的是非 input[type=’file’] 的时,不会弹出选择文件的框。此时可以设置一个隐藏的文件框,通过js来触发这个文件框的点击事件,从而实现文件上传。
<van-cell title="头像" is-link center>
<template #default>
<van-image round class="avatar" :src="profile.photo"
<!-- js触发点击事件 -->
@click="$refs.iptFile.click()"/>
<!-- file 选择框 -->
<input
type="file"
ref="iptFile"
v-show="false"
accept="image/*"
@change="onFileChange"
/>
</template>
</van-cell>
<script>
export default {
methods: {
// 文件选择方法
async onFileChange (ev) {
// console.log(ev.target.files[0])
if (ev.target.files.length === 0) return // 防止用户未选择图片
const fd = new FormData()
fd.append('photo', ev.target.files[0]) // photo在表单里参数名携带
const res = await updatePhotoAPI(fd)
console.log(res)
this.profile.photo = res.data.data.photo // 更新最新头像
}
}
}
</script>
接口
// 用户- 更新头像
// 注意: formObj的值必须是一个表单对象
// '{"a": 10, "b": 20}' // 对象格式的JSON字符串
// new FormData() // 表单对象
export const updatePhotoAPI = (formObj) => {
return request({
url: '/v1_0/user/photo',
method: 'PATCH',
data: formObj
// 如果你的请求体内容是表单对象, 浏览器会自动携带请求头Content-Type为multipart/form-data
})
}
api接口分文件
- 原因: 一个api/index.js, 有几百行代码, 不便于管理
- 解决: 分散到多个js文件里, 再引入回到统一导出
- 分散的js文件名, 尽量和页面模块同名, 方便查找
问题1: 分文件后, 逻辑页面里都是从api/index.js导出的, 难道我们要去改逻辑代码?
解决: 在api/index.js - 中 export * from ‘分散的文件’ (模块重定向)
意思: 在api/index.js 作为入口, 从别的地方把接口倒回来同时导出给外面
export 文档: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/export
注意; 包括reports.js文件也从统一出口导出, 去修改src/components/ArticleItem引数据位置
处理结果 api/index.js如下
export * from './reports' // 反馈列表数据
export * from './ArticleDetail' // 文章详情相关
export * from './Home' // 首页(频道)相关, 首页文章列表
export * from './Login' // 登录相关
export * from './Search' // 搜索相关
export * from './User' / 用户相关
大整数问题处理
JS安全数字范围? 16位的一个数字,超过16位也能显示, 但是精度不准确
定义后台返回数据, 模拟大数
后台数据库id, 生成算法是19位置
const str = '[{"id": 1302900300041101987}, {"id": 1205340366642205763}, {"id": 7689021398237123422}]'
尝试用JSON.parse转换, 发现转换后的值不对
后面3位精度错误
console.log(JSON.parse(str))
原因: JS范围的安全数打印
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
详细原因可以看这里: https://lidongxuwork.gitee.io/pages/webFront/javascript/run/0.1+0.2%E9%97%AE%E9%A2%98.html
解决方案, 可以引入第三方包叫json-bigint
把大数转成字符串保存
npm i json-bigint const jsonBig = require('json-bigint')({ storeAsString: true }) console.log(jsonBig.parse(str))
持久化存储方式
封装本地持久化方法,方便后期统一管理。(无限套娃,哈哈,前面的axios也是这个套路)
- 创建utils/storage.js文件, 定义4个方法
// 本地存储方式
// 如果同时有sessionStorage和localStorage, 可以封装2份
// 现在我只封装一种统一的方式
export const setStorage = (key, value) => {
localStorage.setItem(key, value)
}
export const getStorage = (key) => {
return localStorage.getItem(key)
}
export const removeStorage = (key) => {
localStorage.removeItem(key)
}
export const clearStorage = () => {
localStorage.clear()
}
把所有使用本地存储的地方, 都统一换成这里定义的方法
- 在store/index.js - vuex中使用过
- 在search/index.vue - 搜索页面使用过
抽离组件注册
main.js代码有些多, 分散出去
- 创建src/VantRegister.js, 把Vant注册的相关代码复制过来
import Vue from 'vue'
import { NavBar, Form, Field, Button, Tabbar, TabbarItem, Icon, Tab, Tabs, Cell, List, PullRefresh, ActionSheet, Popup, Row, Col, Badge, Search, Divider, Tag, CellGroup, Image, Dialog, DatetimePicker, Loading, Lazyload } from 'vant'
Vue.use(Lazyload)
Vue.use(Loading)
Vue.use(DatetimePicker)
Vue.use(Dialog)
Vue.use(Image)
Vue.use(CellGroup)
Vue.use(Tag)
Vue.use(Divider)
//....
- 在main.js引入一下, 让代码执行
import './VantRegister'
组件缓存
- 防止组件频繁创建和销毁
- 防止网络请求重复无用执行
组件缓存, 可以实现组件的状态保持。
结合 vue 内置的 keep-alive 组件,可以实现组件的状态保持。
官方文档地址:https://cn.vuejs.org/v2/api/#keep-alive
在App.vue中的router-view外层套上一个keep-alive组件
- 缓存的一级路由页面切换不被释放, 但是首页还是会重新请求数据
在Layout.vue中的router-view外层套上一个keep-alive组件
- 这次Home和User页面都被缓存了(二级路由也要管)
但发现搜索页面和详情页面多被缓存起来了 (多次进入不同的文章, 发现都是同一个文章详情)
对router-view使用exclude属性来区别, 哪些页面组件可以缓存
特别注意exclude里是组件的name名字(跟路由没什么关系)
<template> <div> <keep-alive :exclude="['ArticleDetail', 'Login', 'Search', 'SearchResult']"> <router-view></router-view> </keep-alive> </div> </template>
头像不更新问题
User.vue被缓存了, 所以改了头像回到User页面, created里获取用户资料接口不会执行
解决方案1: 把created换成activated钩子函数即可
解决方案2: UserEdit.vue修改头像成功后, 更新到vuex中, User页面使用的vuex数据也受到更新
登录未遂的处理
要点赞的时候, 401了, 强制跳转到登录页面了, 保存未遂地址跳转到登录页面
要把tokens续签改掉, 否则不会跳转到登录页面
if (error.response.status === 401) { // 身份过期 // token续签方式1: 去登录页重新登录, token无用, 清掉-确保路由守卫if进不去 store.commit('setToken', '') console.log(router.currentRoute.fullPath) //重点1---》将未遂的路径传到登录页 router.push({ path: `/login?path=${router.currentRoute.fullPath}` }) // token续签方式2: refreshToken(用户无感知) // store.commit('setToken', '') // const res = await refreshTokenAPI() // store.commit('setToken', res.data.data.token) // 再调用一次未完成的请求啊(用户无感知) // error.config 就是上一次axios请求的配置对象 // console.dir(error.config) // 把新的token赋予到下一次axios请求的请求头中 // error.config.headers.Authorization = 'Bearer ' + res.data.data.token // return到await的地方 // return ajax(error.config) } else { return Promise.reject(error) }
在Login/index.vue, 登录后, 判断有未遂地址, 跳这里, 否则去/路径
// 跳转到Layout页面 this.$router.replace({ //重点2 登录页进行判断 有未遂地址, 跳这里, 否则去/路径 path: this.$route.query.path || '/layout' })
跨域问题
网页所在url的协议, 域名, 端口号, 和Ajax请求url的协议, 域名, 端口号有一个对应不上, 就发生跨域
跨域是浏览器对ajax做出的限制
常见的跨域问题解决方式
cors方式
前端什么也不用做
后端需要开启cors
实际上就是在响应头添加允许跨域的源
Access-Control-Allow-Origin: 字段和值(意思就是允许去哪些源地址去请求这个服务器)
jsonp方式
需要前端和后端同时支持
前端用script+src属性, 发送函数名给后台, 同时准备好同名的函数, 准备接收数据
后端返回的字符串一定用方法名(数据字符串)格式返回, 到script标签中执行
调用函数名, 并传递数据
例子代码(看看就行, 不用尝试)
<script> function callBackFn(data){ // data就是'{"a": 10, "b": 20}' } </script> <script src="http://后台接口地址?callback=callBackFn"></script> <!-- 后台接口返回 'callBackFn({"a": 10, "b": 20})' -->
代理转发
跨域是浏览器的限制,自己搭的服务器,开启cors后请求就不限制了。
如果后端jsonp也不弄, cors也不弄, 就给你个接口地址
我们可以在本地弄个服务器, 然后用服务器请求后台服务器接口地址
但是vuecli脚手架, 启动了一个webpack开发服务器, 它就能做代理转发
- 而且前端和这个服务器是同源的都是8080端口
需要修改webpack开发服务器的配置即可
更多配置项参考这里: https://webpack.docschina.org/configuration/dev-server/#devserverproxy
devServer: { proxy: { // http://c.m.163.com/nc/article/headline/T1348647853363/0-40.html '/api': { // 请求相对路径以/api开头的, 才会走这里的配置 target: 'http://c.m.163.com', // 后台接口域名 changeOrigin: true, // 改变请求来源(欺骗后台你的请求是从http://c.m.163.com) pathRewrite: { '^/api': '' // 因为真实路径中并没有/api这段, 所以要去掉这段才能拼接正确地址转发请求 } } } }
axios请求的代码
axios({ url: '/api/nc/article/headline/T1348647853363/0-40.html' })
可以使用 http-server 这个node工具来搭建一个代理服务来解决跨域问题
第一步: 使用win +r 打开dos面板,全局安装 npm install -g http-server
第二步: http-server 项目路径 -P 服务器地址
Hbuilder打包app
目标
- 为何要打包APP
- APP分为哪几种类型
分类
App有三大类型
原生的App。手机有两大操作系统:苹果,安卓
还有 windows Phone, 鸿蒙
ios,安卓程序员 用各自的编程语言写的代码,只能在某一个平台上运行。分安卓版本和ios版本。
- 优点:用户体验好 ;可以调用系统API(拍照,读内存…)。
- 缺点:费钱。(大公司一般会雇佣4端程序员)
纯h5网站。就是一个移动站(https://m.jd.com/)
- 优点:省钱。就是网页。
- 缺点:不能调用系统API;没有统一的入口,用户不知道从哪里进来,都要通过浏览器才能访问;
混合开发。
- 先做一个网站,在网站之外套个原生的壳!能同时具备原生的优点和纯h5网站的优点。
- 在原生的App嵌入h5 页
HBuilder开发版
我们需要借助他, 帮助我们打包一个App
下载安装, 注册激活, 如果不注册激活,就不能使用它的打包功能
下载地址: https://www.dcloud.io/hbuilderx.html (下载App开发版)
先走流程, 提示你注册再注册和激活就行了
==必须注册==
==必须激活邮箱==
==必须绑定手机号==
创建5+App项目
我们要选择5+App 项目,mui也是一套前端框架,可以选择一个mui项目。
- 普通项目。 普通H5项目, Hbuilder内置了几套模板,作用不大,同学们基本都会自己创建
- uni-app。多端应用,一套代码,复用八端,时下最火的一个跨端框架
- wap2App。wap项目转 App , 原来只运在手机上的wap(无线网络协议,诺基亚,爱立信时代)项目 可转app项目
- 5+ App。利用DCloud 的 **
5+ Runtime
**来做原生能力提供者的 项目 - 小程序。微信原生小程序的另外一个编辑器,比微信提供的开发者工具好用,但是现在谁还在用原生写小程序呢?
- 快应用 。原生快应用编辑器 , 较为冷门的生态, 目前不太热闹
准备打包
把我们vue项目打包好的dist下的一切复制到你刚才的项目-覆盖过来即可 (一定要保留manifest.json文件)
mainfest.json是打包配置文件
生成APPID
去掉通信录权限 (因为我的HBuilder没有身份证认证, 打包不让获取用户通讯录)
(可选), 如果上面不小心选择No了, 可以去源码处选择 - 删除
云打包
如果一切正常,你将会在控制台中看到类似如下的结果:
这就是云打包成功了, 下面会出现apk下载的所在文件夹
运行
把打包好的apk包, 发到安卓手机上 / 电脑模拟器(推荐<夜神模拟器>) 运行即可
iOS问题
打包ios - 需要申请开发者账号(一年600元人民币): 以后打包的过程参考这个: https://blog.csdn.net/qq_34440345/article/details/99711586
也可以手机给电脑开热点 / 只要连接在同一个wifi下, 手机浏览器访问webpack开发服务器局域网ip地址即可
李老师经验分享
// 写任何需求:
// 章法
// 1. html+css(标签和样式搞定)
// 2. 铺设数据(调整内容, 可能调用接口拿到数据)
// 3. JS(交互/校验….效果), 前端拿到要传递给后台的值
// 4. 与后台交互(调用后台接口, 回显返回数据提示等)
// 技巧1:
// 看到变量, 能马上反应过来这个变量里装的什么
// 每个方法含义, 要什么参数, 返回值有无, 返回值什么意思, 都要马上反应过来
// 每行代码的意思, 为何这么写, 先模仿老师的思路, 锻炼, 多了经验以后就能自己写了
// 以上就多读代码多写代码多讨论积累经验
// 技巧2:
// 前端变量名可以直接跟后端 要求的参数名一致, 这样调用接口就不用再分开写了
// 前端变量名, 如果装对象, 用obj结尾
// 前端变量名, 如果装数组, 用arr或者list结尾
// 前端变量名, 如果装字符串, 用str结尾
// 这样看到变量能马上反应过来里面装的什么
// 技巧3:
// 统一判断http状态码, axios的”响应”拦截器
// axios的”请求”拦截器, 统一给请求配置对象中加入统一的东西
// 例如: 所有的请求都带上请求头字段Authorization和token值
// 技巧4:
// 所有状态一起变的, 一个变量控制所有人
// 每行状态”独立”改变的, 每行对应”对象”里的属性(obj.visible/其他属性), 显示隐藏的状态(2种值切换)
// 技巧5:
// 路由到底是几级的
// 不要光看路径的个数
// 实际:
// 在路由规则数组里的层级
// 技巧6: 什么时候需要提升功能封装
// (1): 多个页面使用的相同功能
// (2): 以后可能要扩展和修改的
// 跨域问题:
// 开发过程:
// 1. 直接让后台开启cors/jsonp, 直接调用(如果用jsonp你要注意你传参的格式)
// 2. 后台不开cors/jsonp, webpack开发服务器, vue.config.js - 代理转发
// 3. 后台不开cors/jsonp, 自己本地node+express搭建服务器(开cors) - 前端请求本地localhost:4005, 本地的请求转发代码(nodejs代码)
// 打包上线:
// 1. 后台开启cors, 直接用
// 2. 后台不开cors/jsonp, 把前端项目和后台项目放在一个服务器上(同源)
// 3. 后台代码和前端代码不在一起, 本地自己写一个node+express服务器部署(请求自己的)