从客户端到前端研发经验总结

一. 概述

笔者是客户端研发出身,如果只有一门技术傍身,不足以胜任市场对客户端研发要求,于是想学习大前端技术栈。但苦于日常工作繁琐,一直没有机会学习(其实就是自身懒惰)。直到工作需要,需要写一个微信小程序,这才下决心学习前端技术。其实如果想入门前端,从微信小程序入手不失为一个好的方法。初次接触微信小程序的数据双向绑定机制,让写习惯了客户端的我叹为观止。目前我入门前端的技术路径是:客户端—微信小程序—360小程序—混合App—H5。一些我自己的经验总结出来,希望对你有所帮助。当然阅读这篇文章的前提是,你已经了解了基本的Html、CSS、JS语法。

二. 环境与工具

1. 前端环境搭建

笔者使用的Mac电脑,所有的环境搭建工作都是基于Mac电脑来操作的。首先安装node.jsnpm:

node.js

node 是一个基于 V8 引擎的 Javascript 运行环境,它使得 Javascript 可以运行在服务端,直接与操作系统进行交互,与文件控制、网络交互、进程控制等。简单的说node.js就是运行在服务端的 JavaScript。你可能会有疑问,我写前端页面为甚么需要一个运行在服务端的的JS环境。其实我们这里使用node最关键是需要安装npm.

npm

npm是node.js的包管理工具(package manager),为啥我们需要一个包管理工具呢?因为我们在Web开发时,会用到很多别人写的JavaScript代码。如果我们要使用别人写的某个包,每次都根据名称搜索一下官方网站,下载代码,解压,再使用,非常繁琐。于是一个集中管理的工具应运而生:大家都把自己开发的模块打包后放到npm官网上,如果要使用,直接通过npm安装就可以直接用。他类似于iOS开发中的cocoapods,Android开发中的Maven,这样就好理解了。
node下载地址点击这里,按照步骤安装完成后,终端输入

node -v
npm -v

查看安装版本,出现下面的版本号说明安装成功。
node

注意如果提示-bash: node: command not found,说明还需要配置一下环境变量。配置方式也很简单,在Finder中查找文件建输入路径/private/etc,找到profile文件,加上一下语句

![pic](/Users/chenxiaoming/Desktop/截屏2021-11-09 14.17.50.png)

export NODE_HOME="node安装路径(bin路径的父级路径)" 
export PATH=$PATH:$NODE_HOME/bin

node安装路径(bin路径的父级路径):你可以通过命令which node 来查看。例如我的本地路径是/usr/local/bin/node,那么可以这样设置

export NODE_HOME="/usr/local"
export PATH=$PATH:$NODE_HOME/bin

重新保存文件后,再次输入node -v 验证一下。下面是一些npm常用命令

// 本地安装模块
npm install <Module Name>
// 全局安装模块
npm install <Module Name> -g
// 搜索模块
npm search  <Module Name>
// 更新模块
npm update <Module Name>
//卸载模块
npm uninstall <Module Name>
// 安装项目的全部依赖
npm install
yarn:

yarn是一个由 Facebook 贡献的 Javascript 包管理器。yarn是为了弥补npm的一些缺陷而出现的。在日常开发中你可以使用npm也可以使用yarn进行包的管理,只不过相比npm而言,它的速度更快,并提供了离线模式。具体关于它的介绍就不多说了,你可以去它的中文网站查看.
它的安装方式也很简单,直接通过Homebrew进行安装 brew install yarn. 它的一些常用命令:

// 初始化一个新项目
yarn init
// 添加依赖包
yarn add [package]
// 添加依赖包的某个版本
yarn add [package]@[version]
// 升级依赖包
yarn upgrade [package]
// 移除依赖包
yarn remove [package]
// 安装项目的全部依赖
yarn install 或者 yarn

2. 开发工具选择

![pic1](/Users/chenxiaoming/Desktop/截屏2021-11-09 15.12.15.png) ![pic2](/Users/chenxiaoming/Desktop/截屏2021-11-09 15.11.54.png)
前端的开发工具基本就两个选择Visual Studio Code 或者 WebStorm

三.Vue项目搭建

1. Vue2余Vue3

四.总结

1. 相关环境配置

NPM 相关操作
npm其经的操作
查看源:npm config get registry 
设置源:npm config set registry https://registry.npmjs.org
升级包:npm update name –save

安装NVM :
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

NVM 是管理node 的工具,安装node-sass爆错 有可能是 node 版本太高了,可以降低一下node 安装版本(注意环境变量的配置),然后在重新yarn install,yarn serve

目前 我的
node -v
v14.3.0

大家对微信小程序都比较熟悉,都知道它是基于微信,不需要下载安装即可使用的应用。其实360也有自己的小程序的平台,不过我们是依托于推推App的。 360小程序的强大之处在于,他可以做到四端打通,即你只需要写一套代码,它可以完美运行在WindowsMaciOSAndroid平台上,极大提高了我们效率,而且后期很有可能以SDK的形式开放出来,让想拥有小程序平台的应用都可以集成这套SDK,这里就不过多介绍了。笔者是客户端研发,小程序的是属于前端开发范畴,在平时的开发中有一些经验总结出来,希望对大家有所帮助。

1. 相关环境配置

NPM 相关操作
npm其经的操作
查看源:npm config get registry 
设置源:npm config set registry https://registry.npmjs.org
升级包:npm update name –save

安装NVM :
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

NVM 是管理node 的工具,安装node-sass爆错 有可能是 node 版本太高了,可以降低一下node 安装版本(注意环境变量的配置),然后在重新yarn install,yarn serve

目前 我的
node -v
v14.3.0

###2. 使用脚手架 搭建小程序

npm_config_registry=http://registry.npm.qiwoo.org npx @q/create-umapp [项目名]

选择相关配置后
$ cd [你的项目目录]
$ npm install

###3. 小程序是基于 Vue 3.0的架构搭建的。注意第三方库的使用要兼容Vue 3.0,我们在使用npm 的时候,去里面搜索。比如在使用Vant时候
Vue3.0跟 2.0是不一样的库

# Install Vant 2 for Vue 2 project
npm i vant -S

# Install Vant 3 for Vue 3 project
npm i vant@next -S

###4. 页面自适应方案
自适应方案:(postcss-px-to-viewport) https://www.cnblogs.com/zhangnan35/p/12682925.html

5. 图片的使用

  • 使用本地img图片
<img
    class="add-btn"
    :src="'./public/images/add.png'"
    alt="添加"
  />
  • 使用 矢量图标:
    导入项目下的 iconfont.js ,然后使用 阿里矢量图库![]

1.引入 iconfont.js

<script src="./iconfont.js"></script>

2. 加入通用 CSS 代码(引入一次就行)在 app.scss 中引入一次即可

<style>
.icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

3.挑选相应图标并获取类名,应用于页面:

<svg class="icon" aria-hidden="true">
  <use xlink:href="#icon-xxx"></use>
</svg>

如果要调整大小、颜色、位置等

.icon {
  font-size: 30px;
        color: orange;
        background: #fff;
        border-radius: 25%;
        position: absolute;
        top: calc(12px + 0.15rem);
        right: 6px;
        vertical-align: 0;
}

加入的图片最好是去掉填充色的,然后你才能修改颜色

4.根据状态渲染不同的图片

7. 网络请求

axios的使用 : axios

Vue 3 安装完成 axios 后 需要在安装Vue-axios

It only has a small benefit that it binds axios to the vue instance so you don’t have to import everytime you use axios

  1. 导入
npm install --save axios vue-axios
  1. 引入
// import Vue from 'vue'   // in Vue 2
import * as Vue from 'vue' // in Vue 3
import axios from 'axios'
import VueAxios from 'vue-axios'
  1. 绑定到框架
    Usage in Vue 2:
Vue.use(VueAxios, axios)

Usage in Vue 3:

const app = Vue.createApp(...)
app.use(VueAxios, axios)

使用方式

 this.axios.get(api).then((response) => {
        console.log(response.data)
      })
      // or
      this.$http.get(api).then((response) => {
        console.log(response.data)
      })

例子:Vue 3.0中的使用

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
import axios from 'axios'
import VueAxios from 'vue-axios'

const app = createApp(App).use(store)
app.use(VueAxios, axios)
app.provide('axios', app.config.globalProperties.axios)  // provide 'axios'
app.mount('#app')

// App.vue
import { inject } from 'vue'

export default {
  name: 'Comp',
  setup() {
    const axios: any = inject('axios')  // inject axios

    const getList = (): void => {
      axios
        .get(api)
        .then((response: { data: any }) => {
          console.log(response.data)
        });
    };

    return { getList }
  }
}

8.获取Dom 元素

方式:

  1. 在标签上添加ref 属性
<div ref = "div"> </div>
  1. 在代码中获取
this.$refs.div.style;

9. 动态绑定类名

  1. 第一种
classname : 是你要绑定的类名
value !== '' : 是判断条件,是 true 或者 false
:class="{classname: value !== '' }"
  1. 绑定多个类命
<div :class="[{ active: isActive }, errorClass]"></div>

// 类名 active
isActive: true || false

// 类名是: error-class, 这里errorClass 是变量名
errorClass: {"error-class" : true}

10. 路由传递参数

  jump(type, json) {
      qh.navigateTo({
        url: `/pages/${type}/index?id=${json.objectiveId}&periodId=${this.periodId}&type=${this.type}`,
      });
      if (type !== 'edit') {
        qh.removeStorage({
          key: 'objData',
          success: () => {
            qh.setStorage({
              key: 'objData',
              data: json,
            });
          },
        });
      }
    },

去目标页面 接收参数

this.$route.query.参数

data() {
    return {
      pkId: this.$route.query.id,
      periodId: this.$route.query.periodId,
      jsonObject: null,
      dragging: false,
     }

11. 页面展示动画

  1. 暂时使用Vant 动画,要使用vant3的库类型

12.修改第三方UI控件的样式时候注意点

修改第三方UI样式类的时候,要去掉scoped关键字
scoped关键字的意思是:该文件的样式只适用于此文件,不会影响到其他控件

<style lang="scss">

13.解决 textarea 输入表情,超出字符限制问题placeHolder,使用 @input

14.iOS input 弹出数字键盘


总的只可以输入数字的input 设置:

<input
        ref="input"
        :readonly="readonly"
        :placeholder="placeholder"
        :type="type"
        v-model="value"
        v-show="!isShowTextarea"
        :pattern="type ==='number'?'[0-9]*':''"
        :onkeyup="onkeyUpDown"
        :onafterpaste="onafterpasteDown"
        :oninput="inputDown"
      />

   onkeyUpDown(){
      if(this.type === 'number'){
         this.value=this.value.replace(/\D/g,'')
      }
    },
    onafterpasteDown(){
      if(this.type === 'number'){
        this.value=this.value.replace(/\D/g,'')
      }
    },
    inputDown() { //限制输入字数
      if(this.title ==='印刷数量(盒)'){
         if(this.value.length>3)
         this.value = this.value.slice(0,3)
      }
    }

15.360小程序查看样式:

终端输入命令 : adb kill-server && adb server && adb shell
谷歌浏览器输入: chrome://inspect/#devices

16. input自适应多行显示,并且能v-model 双向数据绑定逻辑,参考 名片申请card 的AxCustomField.vue

<template>
    <div class="ax-custom-field">
        <van-field 
        class="custom-field" 
        rows="1"  
        :label="title" 
        size="large" 
        :type="type" 
        autosize 
        :required = "required"
        :password ="password"
        :placeholder="placeholder"
        :readonly="readonlay"
        label-class="label-class"  
        input-align="right" 
        :maxlength="maxlength"
        v-model="value" />
    </div>
</template>

<script>
import { computed } from 'vue';
import {Field,CellGroup} from  "vant";
import "vant/lib/field/style";
import "vant/lib/cell-group/style";

    export default {
        name: "AxCustomField",
        data() {
            return {
            }
        },
        components : {
            "van-field" : Field,
            "van-cell-group" : CellGroup
        },
        props : {
            title:String,
            type: {
                type: String,
                default: 'text',
            },
            readonlay: {
                type: Boolean,
                default :false,
            },
            modelValue : {
                 type: [String, Number],
                 default: '',
            },
            placeholder: {
               type: String,
               default: '请输入',
            },
            required: {
                type : Boolean,
                default : false,
            },
            password : {
                type : Boolean,
                default: false,
            },
            maxlength :{
                type : Number,
                default: 100,
            }
        },
        // 组件使用v-model 的方式
        setup(props, { emit }) {
            const value = computed({
             get() {
                return props.modelValue;
             },
            set(value) {
                emit('update:modelValue', value);
            },
            });
            return {
                value,
            };
        },
    }
</script>
<style lang="scss" >
// 如果要自定义组件的UI,使用外部样式类,那么style不能加scoped关键字
.ax-custom-field {
    width: 100%;
    box-sizing: border-box;
}
.custom-field {
    color: black;
    font-size: 16px;
}
.label-class {
    color: black;
    font-size: 17px;
    font-weight: 400;
    width: 110px;
    background-color: white;
}

</style>

17. 判断当前设备的类型:IOS 或者android

    qh.getSystemInfo({
      success: ((res) => {
        this.phoneBrand = res.brand;//手机品牌,Apple 或者 Android
        const  placeholderAppleStr="1、出差原因:拜访客户                                           2、出差中转地:杭州                                                3、特殊说明:周六日延后回京,望领导批复确认。";
        const  placeholderAndroidStr="1、出差原因:拜访客户\n2、出差中转地:杭州\n3、特殊说明:周六日延后回京,望领导批复确认。";
        if(this.phoneBrand == 'Apple'){
          this.placeholderStr = placeholderAppleStr;
        }else{
          this.placeholderStr = placeholderAndroidStr;
        }
      }),
      fail : function(err) {
      }
    })

18.插槽的使用:

普通插槽

// 组件 AxSetKeyPanel里面
 <div>
     <slot></slot>
 </div>

//在主组件使用
<AxSetKeyPanel
        v-for="(item,index) in jsonObject.keyResultList"
        :key="index"
        :title="item.content"
      >
        <AxWeight ---具体插槽
          v-model="item.weight"  在这里可以直接使用 主组件的数据
          :err="item.error"
          :index="index"
          :is-disabled="index===jsonObject.keyResultList.length-1"
          @doCompute="doCompute"
        />
      </AxSetKeyPanel>

具名插槽:是为了解决多个插槽而创建的
使用:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

主组件中使用 v-slot:插槽名称的方式来实现


 <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>

19. input 关键字

<input
        class="l"
        v-model="value"
        inputmode="decimal"
        type="text"
        placeholder="0.0"
        :disabled="isDisabled"
        @input="handleInput" //正在输入
        @blur="handleWeight"// 失去焦点
        @focus="getOldWeight"//得到焦点
      />

<input
        ref="input"
        :readonly="readonly"
        :placeholder="placeholder"
        :type="type"
        v-model="value"
        v-show="!isShowTextarea"
        :pattern="type ==='number'?'[0-9]*':''"
        :onkeyup="onkeyUpDown" //按下键盘
        :onafterpaste="onafterpasteDown"//抬起按键
        :oninput="inputDown"//输入
      />