分类: vue | 标签: vue 移动端

黑马头条-vue移动端项目笔记整理

发表于: 2022-06-30 18:10:28 | 字数统计: 7k | 阅读时长预计: 35分钟

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模式:不采用

image-20220630153712490

css 预处理器: 使用less

image-20220630153737945

eslint语法风格:Standard (一定)

image-20220630153811062

检查节点:保存时检查,提交时检查 (提交时可以不选)

image-20220630153831434

存储插件配置位置:单独放在不同的文件中

image-20220630153904318

接下来,它会问你是否要保存前面的设置作为预设方案,以便后续创建其它项目时直接使用。

如果选择Y, 保存, 以后就可以一键完成以上步骤

经过长长的等待,创建完毕, 进入文件夹, 启动项目

ESLint

  1. 什么是ESLint?

    代码检查工具

  2. 为什么要使用ESLint?

    规范我们写代码的格式, 看着整洁 / 团队内成员风格统一

  3. 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中

image-20220630154354610

注意: 一定要把脚手架工程, 作为vscode根目录, 因为eslint要使用配置文件.eslintrc

  • 一定要配置插件监测的时机, 修改ESLint插件配置

image-20220630154453277

不用管别的, 把红框的放在{}内即可

"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: 后处理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=屏幕宽度的十分之一

总结:

  1. 移动端适配选择哪种?

    rem + flexible.js

  2. flexible.js作用是什么?

    js代码里获取网页宽度 / 10设置html的font-size的值(px单位)

  3. 代码里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

  1. await用于取代then函数, 等待Promise成功结果提取在原地
  2. await无法获取Promise失败的结果, 一旦失败Promise错误直接抛出到控制台

可以利用try和catch解决上面的问题

try {
    // 可能会报错的代码(例如await)
} catch (err) {
    // try里代码报错, 捕捉到这里执行
}

路由

  1. $router和$route区别是?

    • $router下用于跳转路由

    • $route是路由信息对象

  2. 路由的push和replace方法区别?

    • push跳转后, 可以返回
    • replace跳转后, 无法返回
  3. 什么时候用$route.query 什么时候用$route.params

    • $route.params —>动态路由
    • $route.query —>问号传参
  4. 路由的懒加载

路由懒加载 - 查看文档: https://router.vuejs.org/zh/guide/advanced/lazy-loading.html

component: Login
// 改成这个写法
component: () => import('@/views/Login.vue')
  1. 路由守卫(类似后端的拦截器)

方法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位也能显示, 但是精度不准确

  1. 定义后台返回数据, 模拟大数

    后台数据库id, 生成算法是19位置

    const str = '[{"id": 1302900300041101987}, {"id": 1205340366642205763}, {"id": 7689021398237123422}]'
    
  2. 尝试用JSON.parse转换, 发现转换后的值不对

    后面3位精度错误

    console.log(JSON.parse(str))
    
  3. 原因: 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

  4. 解决方案, 可以引入第三方包叫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

  1. App.vue中的router-view外层套上一个keep-alive组件

    • 缓存的一级路由页面切换不被释放, 但是首页还是会重新请求数据
  2. Layout.vue中的router-view外层套上一个keep-alive组件

    • 这次Home和User页面都被缓存了(二级路由也要管)
  3. 但发现搜索页面详情页面多被缓存起来了 (多次进入不同的文章, 发现都是同一个文章详情)

  4. 对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)
    }
    
  1. 在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也不弄, 就给你个接口地址

    我们可以在本地弄个服务器, 然后用服务器请求后台服务器接口地址

    image-20220630174418046

  • 但是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**来做原生能力提供者的 项目
  • 小程序。微信原生小程序的另外一个编辑器,比微信提供的开发者工具好用,但是现在谁还在用原生写小程序呢?
  • 快应用 。原生快应用编辑器 , 较为冷门的生态, 目前不太热闹

image-20220630175509578

准备打包

  1. 把我们vue项目打包好的dist下的一切复制到你刚才的项目-覆盖过来即可 (一定要保留manifest.json文件)

    mainfest.json是打包配置文件

    image-20220630175552604

  2. 生成APPID

    image-20220630175610718

  3. 去掉通信录权限 (因为我的HBuilder没有身份证认证, 打包不让获取用户通讯录)

    image-20220630175632784

  4. (可选), 如果上面不小心选择No了, 可以去源码处选择 - 删除

    image-20220630175650163

云打包

image-20210407171554818

如果一切正常,你将会在控制台中看到类似如下的结果:

image-20220630175836488

这就是云打包成功了, 下面会出现apk下载的所在文件夹

运行

把打包好的apk包, 发到安卓手机上 / 电脑模拟器(推荐<夜神模拟器>) 运行即可

image-20220630175858730

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服务器部署(请求自己的)

------ 本文结束,感谢您的阅读 ------
本文作者: 贺刘芳
版权声明: 本文采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。