Git Product home page Git Product logo

blog's People

Contributors

chenjianou avatar

blog's Issues

linux安装

linux基础

安装

vmware 虚拟机
centos 7.x版本

联网

`vi /etc/sysconfig/network-scripts/ifcfg-ens33`

onboot=yes
:wq 退出

重启网络
service network restart
systemctl restart network
/etc/init.d/network restart

yum包管理

-y 表示无需确认直接安装

  • yum update 系统更新软件
  • yum install wget (安装浏览器软件用)
  • 安装net-tools (能使用ifconfig)
  • 更新镜像源

安装node

  • curl --silent --location https://rpm.nodesource.com/setup_8.x | bash -
    

安装nginx

  • https://nginx.org/en/linux_packages.html
    

    nginx服务配置

    /etc/nginx/nginx.conf

    站点配置

    /etc/nginx/confi.d/default.conf

    重载配置

    /usr/sbin/nginx -s reload

    重启服务

    service nginx restart

    配置防火墙

    /usr/sbin/iptables -F 关闭防火墙(尽量不使用)

    /sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT 开放端口

    /var/log 错误日志

    nginx和node共存

  • 通过反向代理proxy_pass

读书分享 【标杆人生】

每天只读一章

不要单单阅读此书,要与它互动

与好友分享,成熟需要关系的建立与群体的互动产生的

将每日读书纳入我的日程表,当作每天的约会

  • 靠着神的帮助,我承诺在未来四十天内,要寻求神在我生命中的目的。

golang的环境配置

环境配置

安装 golang

  • 从 go 语言官网下载对应的系统版本进行安装
  • 使用Homebrew进行安装(MacOS)

从 go 语言官网下载对应的系统版本进行安装。 go官网是国外的网站,在国内无法下载go语言包 这里推荐大家使用它的镜像
https://golang.google.cn/dl/

brew install go安装

这是比较推荐的两种安装方式,安装完成后,我们在终端输入 go version 的方式进行查看 如果有显示版本号,表示我们安装成功了。

配置环境变量
接下来我们要在系统配置环境变量。
基本上我们要要配置 三个环境变量,GOPATH, GOBIN,PATH和 我们可以通过go env 先查看 当前的配置。

我的电脑是mac,我这边给大家分想mac的环境变量的配置

export GOPATH="${HOME}/go"
export GOROOT=/usr/local/go
export PATH="$PATH:${GOPATH}/bin:${GOROOT}/bin"
export GOPROXY=https://goproxy.cn

GOPATH 工作区设置

比如我的项目放在桌面上 所以 我这边设置的路径就是${HOME}/go

GOROOT 安装路径

我们包安装后的目录,即默认路径 /usr/local/go

GOPROXY 设置代理

由于墙的原因我们没法下载一些包,强烈建议设置。这里推荐的是
https://goproxy.cn ,测试可用。
这是环境的配置
我们通过 vim .bash_profile 进行设置环境变量,配置好后 source .bash_profile
在这个过程中遇到了一个坑,就是如果用的不是自带终端的,比如是我们现在常用的zsh终端的话。
我们需要在.zshrc最后一行添加
source ~/.bash_profile
使环境配置生效 到目前为止所有的配置操作都完成了

进入 vscode 安装 go插件后 command + shift + p 的方式安装go 的 常用工具 安装即可。

swiper项目中的一些坑

当项目中垂直滚动时,内容高度超过一个屏幕时的解决办法

在项目中我们有时候会需要用到当我们一个屏幕中的内容超过一个屏幕的高度,swiper会默认隐藏掉。

.swiper-slide {
    overflow: auto;
}
      var startScroll, touchStart, touchCurrent
      swiper.slides.on('touchstart', function(e) {
        startScroll = this.scrollTop
        touchStart = e.targetTouches[0].pageY
      }, true)
      swiper.slides.on('touchmove', function(e) {
        touchCurrent = e.targetTouches[0].pageY
        var touchesDiff = touchCurrent - touchStart
        var slide = this
        var onlyScrolling =
            (slide.scrollHeight > slide.offsetHeight) && // allow only when slide is scrollable
            (
              (touchesDiff < 0 && startScroll === 0) || // start from top edge to scroll bottom
                (touchesDiff > 0 && startScroll === (slide.scrollHeight - slide.offsetHeight)) || // start from bottom edge to scroll top
                (startScroll > 0 && startScroll < (slide.scrollHeight - slide.offsetHeight)) // start from the middle
            )
        if (onlyScrolling) {
          e.stopPropagation()
        }
      }, true)
this.swiper = new Swiper(this.$refs['swiperRef'], {
            direction: 'vertical',
            slidesPerView :'auto',
            mousewheel: true,
            touchStartPreventDefault: false,
            speed: 600,
            touchStartPreventDefault : false,
            on: {
              slideChangeTransitionStart() {
                if (this.activeIndex === 1 && !this.mapScrolled) {
                  this.mapScrolled = true
                  vm.$refs['mapRef'].$refs['mainRef'].scrollTo(680,0)
                } else if (this.activeIndex === 2 && !this.introduceScrolled) {
                  vm.$refs['introduceRef'].$refs['cardsRef'].scrollTo(40,0)
                  this.introduceScrolled = true
                }
                if (this.activeIndex === 0) {
                  vm.$refs['singRef'].$refs['videoRef'].style.display = 'block'
                }
              },
              slideChangeTransitionEnd () {
                if(this.activeIndex === 1 || this.activeIndex === 2) {
                  vm.$refs['singRef'].$refs['videoRef'].style.display = 'none'
                }
              }
            }
          })

正则学习

正则表达式的其他模式修饰符的用法

1.(?=a):

console.log("我是**人".replace(/我是(?=**)/, "rr"))

打印出:rr**人    (匹配的是**前面的'我是')

2.(?!a):

console.log("我是**人".replace(/(?!**)/, "rr"))

打印出:rr我是**人  

3.(?:a):

console.log("我是**人".replace(/(?:**)/, "rr"))

打印出:我是rr人

4..(?<=a):

console.log("我是**人".replace(/(?<=**)人/, "rr"))

打印出:我是**rr

5.(?<!a):

console.log("我是**人".replace(/(?<!**)/, "rr"))

打印出:rr我是**人

image

参考信息:https://blog.csdn.net/csm0912/article/details/90260296
http://padaker.com/blog/post/5f2136311027605e59d37a3d
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

解决webapp 唤醒后的问题

const ua = window.navigator.userAgent;
const system = {
IOS: /(iPhone|iPad|iPod|iOS)/gi.test(ua),
Android: /android|adr/gi.test(ua)
};
if(system.IOS) {
let self = this
      let time = null;
(function fn(ctx) {
      const handler =setTimeout(() => {
        const now = new Date();
        !time && (time = now);
        const cost = now - time;
        time = now;
        if (cost > 3000) {
          ctx.$toast('我回来了!');
ctx.socket.init();
ctx.$store.dispatch('setUserInfo')
        };
fn(ctx)
      }, 1000)
    })(this);
} else {
function getHiddenProp() {
var prefixes = ['webkit', 'moz', 'ms', 'o'];
// if 'hidden' is natively supported just return it
if('hidden' in document) return 'hidden';
// otherwise loop over all the known prefixes until we find one
for(var i = 0; i < prefixes.length; i++) {
if((prefixes[i] + 'Hidden') in document)
return prefixes[i] + 'Hidden';
}
// otherwise it's not supported
return null;
}
function isHidden() {
var prop = getHiddenProp();
if(!prop) return false;

    return document[prop];
  }
  this.$nextTick(() => {
    document.addEventListener('visibilitychange', (e) => {
      if(isHidden()) {
        this.$toast('我回来了!');
        this.socket.init();
        this.$store.dispatch('setUserInfo')
      }
      // console.log(isHidden() + "-" + new Date().toLocaleTimeString())
    });
  });
}

IOS手机兼容抬起键盘

       <Input autofocus hidden class="absolute left-9999 top-9999"/>
      <form action="javascript:void 0" @keydown.enter="searchKeyUp()" >
        <AutoComplete  ref="input" class="auto-complete" transfer @on-select="onSelect" @on-change="fetchPds" clearable 
                  filterable v-model.trim="queryStr" placeholder="搜索商品名称/SKU ID" style="width: 148px">
          <Option v-for="item in pds" :key="item.pdId" :value="item.pdName">{{ item.pdName }}</Option>
        </AutoComplete>
      </form>

1、通过使用两个input框就能实现,先让第一个input先命中,然后异步的方式foucs另一个input框就能抬起

Rest- Client

Rest- Client

Rest - Client 是一种接口请求工具,类似postman。但是Rest - Client 更加的轻量级,它本身就是一个文件便于保存。

@uri=http://localhost:3000/api // 通过@定义变量

###
GET {{uri}}


###
POST {{uri}}/register
# 文件传输格式  json对象严格遵守 键名要加双引号
Content-Type: application/json

{
  "username": "user2",
  "password": "123456"
}

###
POST {{uri}}/login
# 文件传输格式 
Content-Type: application/json

{
  "username": "user2",
  "password": "123456"
}

### 个人信息

GET  {{uri}}/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMjUyMzNkNzE2OGI0NjIzYTM0NGE2MiIsImlhdCI6MTU3OTQ5NzQxNH0.lgE8hjfjIU5z9hPR0-ES8rDqwZ-PuRrm2y27ZR2MSAo

这就是Rest - Client 的基本使用方式,个人觉得比postman更加好用,在vscode中使用要 进行安装 Rest -Client 插件 使用的文件后缀是.http格式的文件, 是一款很不错的接口测试工具。

vue实现broadcast 和 dispatch

function broadcast (componentName, eventName, params) {
  this.$children.forEach(child => {
    const name = child.$options.name

    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params))
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]))
    }
  })
}
export default {
  methods: {
    dispatch (componentName, eventName, params) {
      let parent = this.$parent || this.$root
      let name = parent.$options.name

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent

        if (parent) {
          name = parent.$options.name
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params))
      }
    },
    broadcast (componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params)
    }
  }
}

usage

// 子组件
this.dispatch('this.dispatch('Form', 'on-form-item-add', this)
//父组件
this.$on('on-form-item-add', (args) => { console.log(args) })

接入创蓝的手机快速登录

1.H5注意事项
在h5登录环境需要输入手机号中间四位
只支持手机移动端,纯手机数据连接场景
h5不支持自定义样式

快速登录展示

image

usage

jmOneKeyLogin({ 
 appKey: "", //tensdk.com 开发者平台应用管理列表中查看
theme: "0" //选填,'0': 全屏 '1': 弹窗
 btnId: "", //按钮标签 id
 }, function (res) {
 // res.code ->'000000':成功
 // res.message ->状态描述
 // res.token ->用于服务端获取手机号码
});

调试成功状态

image

el-upload引发的一些思考学习

这几天的一个项目当中使用到了el-upload组件有些思考,关于组件传递的this指向问题

<el-upload
  action="https://jsonplaceholder.typicode.com/posts/"
  list-type="picture-card"
  :on-preview="handlePictureCardPreview"
  :on-remove="handleRemove">
  <i class="el-icon-plus"></i>
</el-upload>

这是一段element组件库中的上传组件的简单使用。当中我们可以看到当父元素传函数的时候,并不需要改变this。对于大概了解react的我,在react中传递函数的时候都需要绑定this,这是一个让我引起思考的问题。根据js的特性,当谁执行的这段函数的时候,其中的this会指向谁,因此想起了去了解vue中的源码。

 function initMethods (vm, methods) {
    var props = vm.$options.props;
      ....
      vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
    }
  }

截取其中一段比较重要的代码,当实例初始化的时候他的函数便会绑定当前的实例,当传函数时并不会引起this指向的改变问题。

第二个问题,关于el-upload中我们的参数接收的已经是3个固定了但是我们还想自己手动添加参数,我们该如何去做呢。

function handleRemove(err, file, filelist) {
   return function (err,file, filelist, params) {
    //...
  }
}

我们可以通过高阶函数的形式修改原来的函数,添加参数注入。

这是项目中比较有趣的两个问题,记录一下。

windicss 简明分享

为什么使用windicss?

原子类css库,可以复用项目中的css,从而达到缩减项目体积的目的。通过过扫描 HTML 和 CSS 按需生成工具类(utilities),Windi CSS 致力于在开发中提供 更快的加载体验 以及更快的 HMR,并且在生产环境下无需对 CSS 进行 Purge(一种在生产环境中对未使用的 CSS 进行清除而节省体积的技术)。
VSCODE 插件
windicss IntelliSense - windicss 提示插件

自动值推导

image

在编译后会生成对应的css样式
可变修饰组( 响应式、主题(dark、light)))
image

https://windicss.org/utilities/general/variants.html#orientation-variants 变体

Shortcuts

工具类集合
image

Important 前缀

import 权重!!

指令

@apply
image

不同写法

参考地址
https://windicss.org/guide/
https://www.tailwindcss.cn/

关于element casacder的回显

<template>
  <el-cascader style="width:100%" :filterable="true" v-model="currentValue" :options="selectOptions" :placeholder="placeholder" :clearable="clearable" :show-all-levels="showAllLevels" :props="props" :disabled="disabled" ref="cascader"></el-cascader>
</template>

<script>
import mixModel from './mixModel'
export default {
  name: 'PostCategorySelect',
  mixins: [mixModel],
  props: {
    maxCount: { Number, default: 1 },
    labelKey: { String, default: 'value' },
    valueKey: { String, default: 'id' },
    showAllLevels: { Boolean, default: true },
    clearable: { Boolean, default: false }
  },
  data () {
    return {
      selectOptions: []
    }
  },
  watch: {
    currentValue (nv) {
      // 选中选项后收起下拉框
      console.log(nv)
      this.$refs['cascader'] && this.maxCount === 1 && (this.$refs['cascader'].dropDownVisible = false)

      let allOptions = this.$refs['cascader'].$refs['panel'].store.getNodes()
      if (nv && nv.length >= this.maxCount) {
        allOptions.forEach(i => {
          i.children && i.children.forEach(j => this.setOptionEnabledStatus(j, true))
        })
      } else {
        allOptions.forEach(i => {
          i.children && i.children.forEach(j => this.setOptionEnabledStatus(j, false))
        })
      }
    }
  },
  computed: {
    props () {
      return {
        expandTrigger: 'hover',
        label: this.labelKey,
        value: this.valueKey,
        multiple: this.maxCount > 1,
        emitPath: true,
        lazy: true,
        checkStrictly: true,
        lazyLoad: (node, resolve) => {
          if (node.level === 0) {
            this.getPositions().then(options => {
              options.forEach(i => {
                i.leaf = false
                i.disabled = true
              })
              resolve(options)
            })
          } else if (node.level <= 2) {
            this.getChildPostions(node.data.code).then(options => {
              options.forEach(i => {
                i.leaf = node.level >= 2
                i.children = []
                i.disabled = (this.maxCount > 1 && this.currentValue && this.currentValue.length >= this.maxCount) || false
              })
              resolve(options)
            })
          } else {
            resolve()
          }
        }
      }
    }
  },
  methods: {
    getPositions () {
      return this.api.getPositionList({ parentcode: -1 }).then(res => {
        return res.data.data
      })
    },
    getChildPostions (parentcode) {
      return this.api.getPositionDetail({ parentcode }).then(res => {
        return res.data.data
      })
    },
    setOptionEnabledStatus (option, value) {
      if (this.currentValue.indexOf(option.value) === -1 && this.maxCount > 1) {
        option.data.disabled = value
      }
      option.children && option.children.forEach(i => this.setOptionEnabledStatus(i, value))
    }
  }
}
</script>

浅谈mixins、混入

vue的mixins是混入的意思、即这个代码可以在vue任意的组件里进行使用,而他的this也会指向他的当前组件。我们可以在全局或者局部去引用mixins。
在全局使用mixins是

  computed: {
    uploadUrl(){
      return this.$http.defaults.baseURL + '/upload'
    }
  },
  methods: {
    getAuthHeaders(){
      return {
        Authorization: `Bearer ${localStorage.token || ''}`
      }
    }
  }
})

我们经常会以这样的方式进行全局注册mixins,
而我们在项目中也会使用局部的mixins, 我们在使用mixin的时候也会因为这不是一个主要的代码逻辑、而且冗余繁多的时候将代码提取出来用mixins进行编写,这个时候我们就用到局部的mixins了
代码示例
export default { }
将对象暴露出去
组件中

 export default {

 mixins : [ minxins]
}
</script>

这样的方式将代码进行引入,实际上在代码中使用method等属性也是一样的,我们这样的方式的话也是可以很好的,将代码的进行提取,整体页面会更加清晰整洁。我们在用局部的mixins的时候也不会将主要逻辑写在mixins中。

css变量的使用

:root {
    --color:#000;
    --Color:#001;
//作用域的变量
}
      const root = getComputedStyle(document.documentElement)  
      const cssVariable = root.getPropertyValue('--line-width').trim()
      document.documentElement.style.**_setProperty_**('--line-width', this.$refs['tabsRef'][index].clientWidth + 'px')

// 获取css样式属性, getComputedStyle是只读属性,不可以设置
// 通常我们可以通过 document.documentElement.style 的样式表去查询变量或改变变量

vue使用示例

<template>
  <div class='course-page'>
    <div class='course-filters'>
      <div class='course-item' ref='tabsRef' :class='{ active: item === current }' @click='handleTabChange(item, index)' v-for='(item, index) in tabs' :key='item.id'>{{item.text}}</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tabs: [],
      current: null
    }
  },
  methods: {
    handleTabChange(item, index) {
      **const root = document.documentElement.style
      root.setProperty('--line-width', this.$refs['tabsRef'][index].clientWidth + 'px')**
      this.current = item
    }
  },
  mounted() {
    this.current = this.tabs[0]
  }
}
</script>
<style>
**:root {
  --line-width: 27px;
  --color: red;
}**
</style>
<style lang='scss' scoped>
.course-page{
  .course-filters {
    display: flex;
    width: 350px;
    justify-content: space-between;
    padding-left: 20px;
    .course-item {
      color: #4A4A4A;
      padding-bottom: 4px;
      &.active {
        color: #f9791b;
        font-weight: 600;
        position: relative;
        &::after {
          content: '';
          position: absolute;
          bottom: 0;
          left: 0;
          **width: var(--line-width);**
          height: 4px;
          background: #F9791B;
          border-radius: 2px;
        }
      }
    }
  }
}
</style>

vue兼容iE9+

兼容ie9+高版本

如果项目中使用了第三方组件请添加

// vue.config.js
module.exports = {
  // 如下正则代表当前node_modules/zzp-ui下的所有文件【如果是字符串只包含引用的一个文件】
  transpileDependencies: [/zzp-ui/]
}

建议在 public/index.html(模板) head 添加:

<meta name="renderer" content="webkit|ie-comp|ie-stand">

首先强制浏览器使用webkit内核(极速内核)
如果浏览器没有webkit内核,则按照用户浏览器所支持的最新的ie的trident内核渲染页面(ie兼容内核)
否则按照当前浏览器的标准内核渲染(ie标准内核)


IE9 默认不支持跨域访问,如项目使用到跨域且需要兼容IE9, 请配置nginx等转发网络请求

本地开发如在IE9下测试解决跨域可临时使用proxy

module.exports = {
  // productionSourceMap: false, // 生产环境的 source map
  devServer: {
    // host: '0.0.0.0',
    // port: '8080',
    // disableHostCheck: true,//解决127.0.0.1指向其他域名时出现"Invalid Host header"问题
    proxy: {
      '/api': {
        target: 'http://taxt.wisejob.stu.syedu.tech',
        ws: true,
        changeOrigin: true
      }
    },
  },
  // transpileDependencies: [/shengya-ui/]
}

直接检测是ie,让升级浏览器: 以下代码是是淘宝的判断逻辑

<script type="text/javascript">
   // 判断是否IE(6~11)
   var isIE = window.ActiveXObject || "ActiveXObject" in window
   if (isIE) {
     window.location = 'https://ipublish.tmall.com/browser.htm';
   }
</script>

实现一个弹窗Vue组件


import ToastVue from './Toast.vue'
let Toast = {}
Toast.install = function (Vue, options) { //install 才能被Vue.use
  let opt = { // 设置默认配置
    defaultType: 'center',
    duration: 1500
  }
  for (let prop in options) {
    opt[prop] = options[prop] //传入的值 
  }
  Vue.prototype.$toast = ({ message, type }) => {
    if (type) {
      opt.defaultType = type
    }
    let ToastTpl = Vue.extend(ToastVue)
    let tpl = new ToastTpl({
      propsData: {
        type: type,
        message: message
      }
    }).$mount().$el // 实现手动挂载
    document.body.appendChild(tpl)
    setTimeout(() => {
      document.body.removeChild(tpl)
    }, opt.duration);
  }
  ['success', 'warning', 'error'].forEach(type => {
    Vue.prototype.$toast[type] = (message) => {
      return Vue.prototype.$toast({ type, message })
    }
  })
}
export {
  Toast
}

前端性能优化【函数节流、防抖】

//只会触发最后一次 ,只认最后一次,从最后一次触发开始计时。
function debounce (func, delay) {
  let timer = null
  //...arg 收集
  return function (...arg) {
    let context = this
    clearTimeout(timer)
    timer = setTimeout(() => {
      func && func.apply(context, arg)
    }, delay);
  }
}

// 节流函数时间戳
function throttle(func, wait) {
  let context, args
  let previous = 0; 
  return function () {
    let now =+ new Date()
    context = this
    args = arguments
    if (now - previous > wait) {
      func.apply(context, args)
      previous = now;
    }
  }
}

//定时器版本
function throttle(func, wait) {
  let timeout,context, args
  return function () {
    context = this
    args = arguments
    if (!timeout) {
      setTimeout(() => {
        func.apply(context, args)
        timeout = null
      }, wait);
    }
  }
} 
//立即执行版本
function throttle(func, wait) {
  let timeout,context, args
  return function () {
    context = this
    args = arguments
    if (!timeout) {
      timeout = setTimeout(() => {
        timeout = null
        //删除 func.apply(context, args)
      }, wait)
      func.apply(context, args)
    }
  }
} 

-- 节流函数可以设置立即执行版和非立即执行版本,在代码中并未展示,记录下代码,给自己看看。

typescript基本语法

� typescript 安装

  • typescript 是 JavaScript 的超集
  • 通过全局安装 npm install -g typescript 安装
  • 编译方式 tsc xxx.js
    2、 实时代码更新
  • 通过 typescript 的 tsc 命令 tsc --init 生成 tsconfig 文件
  • 修改"outDir"的输出路径 如 outDir: "./js" 这个文件夹下边

typescript 数据类型

  • typescript 为了使代码编写更规范,有利于维护, 增加类型校验
    布尔类型(boolean)

var boo: boolean = false

数字类型(Number)

var num: number = 1

字符串 (string)

var str: string ='123'

数组 (array)

var arr: number[] = [1,3,4,5]
var arr: Array<Number>= [11,33,55] 规定了数组的类型

元组 (tuple)

let arr: [number,string] = [111,333,"2212"]

枚举类型 (enum)

enum Flag{success=1,error=2}
var c:Flag = Flag.success 如果不赋值结果为下标

任意类型 (any)
void 类型
never 类型

写 ts 代码必须指定类型

第三方数据统计工具【友盟为例】

https://www.umeng.com/ 【友盟】

在vue中使用

  mounted () {
    const script = document.createElement('script')
    script.src = 'https://s95.cnzz.com/z_stat.php?id=1111111111&web_id=1111111111'
    script.language = 'JavaScript'
    document.body.appendChild(script)
  },
  watch: {
    '$route' () {
      if (window._czc) {
        let location = window.location
        let contentUrl = location.pathname + location.hash
        let refererUrl = '/'
        window._czc.push(['_trackPageview', contentUrl, refererUrl])
      }
    }
  }

usage

document.querySelect(.onclick).addEventListener('click',()=>{
window._czc.push(["_trackEvent", category, action, label, value, id]);
})

vue函数式组件、render函数

函数式组件,它是无状态 (没有响应式数据),无实例 (没有 this 上下文)。所以它的引入就是为了方便开发者定义很逻辑清晰的组件。并且在vue组件中,template的优先级比render的优先级要高,所以我们在使用的时候,不能同时使用template和render。《这是我在刚开始学习的render函数时遇到的坑》
所以在我们平时写项目过程中。使用render函数的话基本上是可以在js文件使用

export default {
        functional: true // 设置函数式组件
        props:{} ,   //是可选项
        render(h,context) {
            return h('comp', {}, context.children )
          }
}
//函数式组件

props:提供所有 prop 的对象
children: VNode 子节点的数组
slots: 一个函数,返回了包含所有插槽的对象
scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
parent:对父组件的引用
listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

//普通的render函数
```javascript
{
  // 与 `v-bind:class` 的 API 相同,
  // 接受一个字符串、对象或字符串和对象组成的数组
  'class': {
    foo: true,
    bar: false
  },
  // 与 `v-bind:style` 的 API 相同,
  // 接受一个字符串、对象,或对象组成的数组
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 普通的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 组件 prop
  props: {
    myProp: 'bar'
  },
  // DOM 属性
  domProps: {
    innerHTML: 'baz'
  },
  // 事件监听器在 `on` 属性内,
  // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
  // 需要在处理函数中手动检查 keyCode。
  on: {
    click: this.clickHandler
  },
  // 仅用于组件,用于监听原生事件,而不是组件内部使用
  // `vm.$emit` 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 Vue 已经自动为你进行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // 作用域插槽的格式为
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果组件是其它组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  // 其它特殊顶层属性
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
  // 那么 `$refs.myRef` 会变成一个数组。
  refInFor: true

fastclick 移动端点击延迟解决

usage

var attachFastClick = require('fastclick');
attachFastClick(document.body);

needsclick

对于页面上不需要使用fastclick来立刻触发点击事件的元素在元素标签的class上添加needsclick

ES5实现继承

###构造函数继承

通过call改变this指向实现继承

function Parent (){
   this.name='xiaohua'
}
function Child (){
   Person.call(this)
   this.height = '180px'
}

new Child ()

这样的问题是我们虽然能拿到Parent的属性值但是无法拿到原型上的方法。对此我们引出第二种继承方式

原型继承

function Parent (){
   this.name='xiaohua'
}
function Child (){
   this.height = '180px'
}
Child.prototype = new Person

child = new Child ()

child2 = new Child2()

通过上面原型继承的方式我们可以得到该父类属性和方法。但是还有一个问题,当我们修改继承原型的属性时候,会影响到其他的子类。

组合继承(构造函数+原型)

function Parent (){
   this.name='xiaohua'
}
function Child (){
   Person.call(this)
   this.height = '180px'
}
Child.prototype = new Parent

child = new Child ()

通过这种方式可以解决,但是又多了一个问题,我们这里就多执行了一次Parent(Child.prototype = new Parent),

组合继承优化

function Parent (){
   this.name='xiaohua'
}
function Child (){
   Parent.call(this)
   this.height = '180px'
}
Child.prototype = Parent.prototype
child = new Child ()

Child的constructor指向出问题了

组合继承优化2(圣杯模式)

function Parent (){
   this.name='xiaohua'
}
function Child (){
   Parent.call(this)
   this.height = '180px'
}
 Child.prototype = Object.create(Parent.prototype); // es6方法Object.create
  Child.prototype.constructor = Child;

child = new Child ()

通过上面的案例我们可以自己实现一个继承方法inherit

//实现ES6 Object.create
function createObj  (o){
   let F = function (){}
  F.prototype = o
  return new o
}
// 父类
function SuperType(){
}

//子类
function Sub(){
   SuperType.call(this)
}

//继承
let inherit = function (target, origin){
   const prototype= createObj(origin.prototype)
   target.prototype = prototype
   target.prototype.constructor = target
   target.prototype.uber = origin
}

inherit(Sub, SuperType)

打完收工!!!

参考资料:https://juejin.cn/post/6844903872847151112

关于vue表单项目的一些总结

1、这是一次表单的总结。因为我们在页面中有使用很多组件进行切换时,我们采用了将每个每块拆分进行,也就是使用组件的方式进行
2、同时 我们进行修改也是用父组件进行修改。在vue中的v-model是可以实现父子组件双向绑定的。所以我们可以使用这样的方式将父组件的对象传下去。
3、在使用element的form组件的时候,我们没办法在父组件进行判定或者说是表单验证的方法,即 form.validate((boo,obj)=>{})的方法进行验证,我们可以通过this.$refs['ComponentName'].validate()的方式调用子组件的方法。

<template>
  <div class="information">
    <HomeTitle title="填写引导">
      <div class="review" @click="$router.push({name:'VitaeView' , query:{ templetId}})">
        <img src="@/assets/images/curriculum/preview.png">
      </div>
      <template v-slot:text>
        <div class="temp">
          正在使用:{{templetName}}
        </div>
      </template>
    </HomeTitle>
    <div class="info-header">
      <div class="text">{{sideItem[currentSide]}}</div>
    </div>
    <div class="main">
      <div class="sidebar">
          <div class="side-item">
            <div v-for="(item, index) in sideItem" :key="item" @click="changeSide(item,index)" class="side " :class="{active: currentSide === index}"><span>{{item}}</span></div>
          </div>
      </div>
      <div class="content"  >
        <div v-if="resume">
          <BaseInfo v-if="currentSide === 0" ref="baseInfo" :model="resume"/>
          <Objective v-if="currentSide ===1" ref="objective" :model="resume" />
          <Describe v-if="currentSide === 2" :model="resume"  placeholder="请在此填入您的个人总结" nature="summary"/>
          <Internship v-if="currentSide ===3" :model="resume['resumePracticeDto']"/>
          <Education v-if="currentSide === 4" :model="resume['resumeEducationDto']"/>
          <Describe v-if="currentSide === 5"  :model="resume" placeholder="讲讲在学校里做过什么吧" nature="campusExperience"/>
          <Other v-if="currentSide === 6 && resume "  :skill="resume['resumeSkillDto']" :certificate="resume['resumeCertificateDto']"/>
        </div>
      </div>
      <div class="right-tips">
          <TipsInfo :info="currentDataInfo"/>
      </div>
    </div>
  </div>
</template>

<script>
import HomeTitle from '@/components/interview/HomeTitle'
import BaseInfo from './components/BaseInfo'
import TipsInfo from './components/TipsInfo'
import Internship from './components/Internship'
import Objective from './components/Objective'
import Education from './components/Education'
import Describe from './components/Describe'
import Other from './components/Other'
import mixins from './mixins/mixinValidate'
import formMixins from './mixins/formValidate'
export default {
  name: 'infomation',
  mixins: [mixins, formMixins],
  data () {
    return {
      sideItem: ['基本信息', '求职意向', '自我介绍', '实习经历', '教育经历', '校园经历', '其他信息'],
      tipsInfo: ['foundation', 'job', 'oneself', 'practice', 'education', 'campus', 'other'],
      tipsData: [],
      currentSide: 0,
      currentDataInfo: [],
      // 用户详情
      id: 106,
      resume: {},
      templetId: -1,
      templetName: null
    }
  },
  methods: {
    async saveResume () {
      const res = await this.api.updateResume({ id: this.$route.query.id, ...this.resume })
      if (res.data.message === '成功') {
        this.$message({
          type: 'success',
          message: '信息保存成功'
        })
      }
    },
    changeSide (item, index) {
      if (this.currentSide !== index) {
        this.formValidate(this.sideItem[this.currentSide], () => {
          this.validate(index)
        })
      }
      this.currentDataInfo = this.tipsData[this.tipsInfo[this.currentSide]]
    },
    getResumeDetail (id) {
      return this.api.getResumeDetail({ id }).then(res => {
        this.resume = res.data.data
        return res.data.data
      })
    }
  },
  async created () {
    if (this.$route.query.id) {
      const data = await this.getResumeDetail(this.$route.query.id)
      const res = await this.api.getTemplateTips({ id: 1 })
      this.templetId = data.templetId
      this.templetName = data.templetName
      this.tipsData = res.data.data
      this.currentDataInfo = res.data.data[this.tipsInfo[0]]
    }
  },
  beforeRouteLeave (to, from, next) {
    this.saveResume()
    next()
  },
  components: {
    HomeTitle,
    TipsInfo,
    BaseInfo,
    Internship,
    Objective,
    Education,
    Describe,
    Other
  }
}
</script>

<style lang="scss" scoped>
.information {
  .review {
    margin-right: 150px;
    cursor: pointer;
  }
  .temp {
    font-size: 14px;
    color: #585e70;
    transform: translate(-20%);
  }
  .info-header {
    height: 110px;
    display: flex;
    justify-content: center;
    align-items: center;
    > .text {
      font-size: 22px;
      color: #585e70;
      font-weight: 600;
      position: relative;
      &::before,
      &::after {
        content: '';
        position: absolute;
        width: 15px;
        height: 15px;
        top: 50%;
        transform: translate(0, -50%);
        background: url('../../../assets/images/curriculum/solid-line.png')
          no-repeat;
      }
      &::before {
        left: -30px;
      }
      &::after {
        right: -30px;
      }
    }
  }
  .main {
    display: flex;
    .sidebar {
      margin-left: 111px;
      width: 299px;
      min-width: 226px;
      height: 568px;
      box-shadow: 0px 0px 30px 0px rgba(74, 94, 153, 0.02);
      background: #ffffff;
      border-radius: 8px;
      display: flex;
      justify-content: center;
      .side-item {
        .side {
          width: 120px;
          margin: 50px auto 0;
          text-align: center;
          font-size: 17px;
          color: #585e70;
          cursor: pointer;
          &.active {
            border-left: 6px solid #ff8f4b;
            color: #ff8f4b;
          }
        }
      }
    }
    .content {
      flex: 1;
      min-height: 628px;
      background: #ffffff;
      box-shadow: 0px 0px 30px 0px rgba(74, 94, 153, 0.02);
      border-radius: 8px;
      margin: 0 30px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
  }
}
</style>

js 数组和树转化

这两天在弄数据库,省市县镇的数据联动,拿到的数据是

{
parentCode: 0,
name: 北京市,
code: 110000,
children: [
parentCode: 110000,
name: 市辖区,
code:110100,
children: []
]

是这样的一个数据结构当我们需要将这样的数据存到数据库我们就需要将这个数据扁平化,在考虑嵌套层级可能是无限级别的,我们这里就比较适合用递归的方式去实现,扁平化

function flatten (pcsa) {
  let res = []
  for (let index = 0; index < pcsa.length; index++) {
    const item = pcsa[index];
    provinceSql.push({
      code: item.code,
      name: item.name,
      parentCode: item.parentCode,
    })
    if (item.children) {
      res = provinceSql.concat(...flatten(item.children))
    }
  }
  return res
}

//然后我们再尝试将数据进行还原
function array2Tree(data, root) {
  let res = []
  data.forEach((item) => {
    if (item.parentCode === root.code) {
      let children = array2Tree(data, item)
      if (children.length) item.children = children
      res.push(item)
    }
  })
  return res
}  
// usage
const res = array2Tree(flatProvince, { code: '0' })

总得来说还是不是很难,但是花了自己两天时间在上面还是简单的记录下。

Table组件规则校验问题,以及Form动态校验 (ELEMENT, IVIEWUI)

<-- 动态校验 -->
<Form ref="formDynmic" :model="{ landPageTableItemInputList: item.landPageTableItemInputList }">
          <div class="tab-header__menu">
            <FormItem class="tab-item_btn" v-for="(row, index) in item.landPageTableItemInputList" :key="row"
            :prop="'landPageTableItemInputList.' + index + '.tabName'"
            :rules="[{ required: true, message: 'tab名称不能为空' }, { pattern:/^[A-Za-z0-9\u4e00-\u9fa5]+$/, message: '只能输入中文,数字,英文', trigger: 'blur'}]">
              <Icon class="icon-close" type="ios-close" @click="handlerTabItemDelete(row, index)" />
              <Input maxlength="8" v-model="row.tabName" placeholder="tab"/>
            </FormItem>
          </div>
        </Form>

svg

<title>Document</title> <style> .horse-run { /* 之前语法 始于2015年9月,M58版本会移除,大约2017年4月 */ motion-path: path("M10,80 q100,120 120,20 q140,-50 160,0");
    /* 当前规范上的语法 2016年12月支持 */
    offset-path: path("M10,80 q100,120 120,20 q140,-50 160,0");

    animation: move 3s linear infinite;
}

@keyframes move {
  /* 之前语法 */
  100% { motion-offset: 100%;}
  /* 当前规范语法 */
  100% { offset-distance: 100%;}
}
img {
  position: absolute;
}
</style>

video标签的一些简单的兼容

<video
// 设置后,音频会初始化为静音,注意浏览器只有设置静音,才能自动播放
 muted
// 视频会马上自动开始播放,不会停下来等着数据载入结束。
autoplay="autoplay"
// 布尔属性;指定后,会在视频结尾的地方,自动返回视频开始的地方
 loop="loop"
// 一个布尔属性,标志视频将被“inline”播放,即在元素的播放区域内。
x5-playsinline="true"
playsinline="true"
webkit-playsinline="true"
// 一个布尔属性,用于禁用使用有线连接的设备(HDMI、DVI等)的远程播放功能。
x-webkit-airplay="allow"
// 这个视频优先加载
preload="auto"
// 启用同层H5播放器,就是在视频全屏的时候,div可以呈现在视频层上,也是WeChat安卓版特有的属性。同层播放别名也叫做沉浸式播放
x5-video-player-type="h5"
// :全屏设置。它又两个属性值,ture和false,true支持全屏播放
x5-video-player-fullscreen="true"
>
// <source> 标签为媒介元素(比如 <video><audio>)定义媒介资源。
<source src="indexMove.mp4" type="video/mp4">
</video>

复制功能实现

function h5clipboard (val) {
			let textArea = document.createElement("textarea");
			textArea.value = val
			document.body.appendChild(textArea);
			textArea.select();
			try {
				let successful = document.execCommand('copy');
				if (successful) {
					Toast.success("成功复制到剪贴板");
				} else {
					Toast.warning("该浏览器不支持点击复制到剪贴板");
				}
			} catch (err) {
				Toast.warning("该浏览器不支持点击复制到剪贴板");
			}
			document.body.removeChild(textArea);
}

关于vue的自定义v-model

在其他一些框架语言中并没有v-model这么强大的指令,这么方便。在react使用value,onchange的方式实现双向绑定明显麻烦很多。所以在学习vue的基础语言语法相对其他来说确实简单了很多了。
在写这篇文章前,因为业务需要自定义组件的封装,就去看了这个关于v-model的自定义组件的实现,我们先看看官方文档的说明。
从官方和技术文章中我们可以知道,v-model是v-bind以及v-on配合使用的语法糖,举个例子

  <input v-model="value"/>
  <input v-bind:value="value" v-on:input="value=$event.target.value">

这两种方式实现是等价的
现在我们已经了解了v-model是什么东西了,我们除了能在表单组件上实现v-model,能不能在自定义组件实现双向绑定呢呢??

答案是可以的。
vue官方文档的v-model的说明:一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。
也就是说 我们在在自定义组件上使用v-model 的时候 也是 v-bind:value , v-on:input="value=$event.target.value"
这是默认的v-model的形式,我们也可以通过在子组件中的 model上自定义他们的value和event,接下来我们看代码。

//子组件
<template>
  <div>
    <input  type="text" v-model="checkData">
  </div>
</template>

<script>
export default {
  props: {
    value: {
      default: ''
    }
  },
  computed: {
    inputData: {
      get () {
        return this.value
      },
      set (val) {
        this.$emit('checked', val)
         //这个是computed的get  set不是我们的重点, 简单来说就是 得值和改值时触发的事件
      }
    }
  },
  model: {
    prop: 'value',
    event: 'checked' // 这里是我们通过修改默认的value、 input事件 改成我们要使用 checked
  }
}
</script>
// 父组件
<template>
  <div>
    {{msg}}
    <a-input v-model="msg"></a-input>
  </div>
</template>

<script>
import Input from './Input.vue'
export default {
  data () {
    return {
      msg: '12321'
    }
  },
  methods: {
  },
  components: {
    aInput: Input
  }
}
</script>

<style lang="scss" scoped>
</style>

在上面中我们可以看到v-model在自定义组件的实现。 通过$emit的方式,这样是我们很常见的组件通信、我们通过这样的方式传值。所以在这里我们就修改的值传到父组件去修改了。 我们这里是用computed去实现也是可以的,我们也可以用很多实现的方法,当我们修改的时候,比如watch去触发,核心的还是$emit 将我们想要传的值传给父组件, 还有就是model里的event设置跟$emit对应即可。
这两段代码简单实现了自定义组件的v-model,接下来我们就可以愉快的使用自定义组件的v-model啦

最后还有个.sync修饰符比较类似这个v-model有兴趣的小伙伴可以去看看哦!!!

编者第一次写,如果有不足之处欢迎指正!如果有上面问题也欢迎提问哦

前端下载图片功能

function drawImage(src: string): Promise<string> {
  let image: HTMLImageElement = new Image,
    canvas: HTMLCanvasElement = document.createElement('canvas'),
    ctx = canvas.getContext('2d');
  image.setAttribute('crossOrigin', 'Anonymous');
  image.src = src;
  return new Promise((resolve) => {
    image.onload = () => {
      let { width, height } = image;
      canvas.width = width;
      canvas.height = height;
      ctx!.drawImage(image, 0, 0);
      canvas.toBlob((blob) => {
        let url = URL.createObjectURL(blob);
        resolve(url)
      });
    }
  })
}
function download(src: string, name?: string) {
  let a = document.createElement('a');
  let realName: string = name || src;
  a.download = realName;
  document.body.appendChild(a)
  drawImage(src).then(url => {
    a.href = url;
    a.click();
    URL.revokeObjectURL(url);
  });
}
export {
  download
}

Javascript 模拟事件点击与长按的实现

模拟点击事件

      //方案一
      var oDiv = document.getElementsByClassName('btn')[0]
      oDiv.onclick = function(e) {
       //该特性要被丢弃了
        let evt = document.createEvent('MouseEvents')   //创建 event对象
        evt.initMouseEvent('click', false, false)  //type, bubble, cancelable
        document.getElementById('file').dispatchEvent(evt) // 给元素派发事件
      }
      // ****重要***
    oDiv.onclick = function(e) {
      var input = document.createElement('input')
      input.setAttribute('type', 'file')
      input.click()
      input.onchange = function(e) {
        console.log(input.files[0])
        let form = new FormData()
        console.log(form)
        form.append('file', input.files[0])
        //axios.post('/api/upload',form,config).then(res=>{})
      }
    }

实现longPress

    let timeOutEvent = null

    document.addEventListener('touchstart', function(e) {
      //点击任意都能获得当前元素
      timeOutEvent = setTimeout(function() {
        timeOutEvent = 0
        // 点击给当前元素派发事件
        e.target.dispatchEvent(new CustomEvent('longpress'))
      }, 400)
    })
    //移动
    document.addEventListener('touchmove', function(e) {
      clearTimeout(timeOutEvent)
      timeOutEvent = null
    })
    //弹起清空
    document.addEventListener('touchend', function(e) {
      clearTimeout(timeOutEvent)
      timeOutEvent = null
    })

usage

    
    document
      .getElementsByClassName('btn')[0]
      // 监听当前元素事件触发的回调
      .addEventListener('longpress', function() {
        console.log('ok longpress')
      })

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.