vue3.0 搭建项目总结(详细步骤)


Posted in Javascript onMay 20, 2019

1.环境配置

项目中的不同开发环境有很多依赖配置,所以可以根据环境设置不同的配置,以免在不同环境经常修改文件

1 在根目录下创建 `.env.[环境]` 文件,可以在不同环境设置一些配置变量,如图

vue3.0 搭建项目总结(详细步骤)

vue3.0 搭建项目总结(详细步骤) 

.env.dev 文件

2.eslint 配置

在package.json 文件里面有一个eslintConfig对象,可设置rules: 如图

vue3.0 搭建项目总结(详细步骤)

3.配置svg

在vue.config.js 里面需在module.exports对象里面设置

chainWebpack: config => {
  config.module.rules.delete('svg') // 重点:删除默认配置中处理svg,//const svgRule = config.module.rule('svg') //svgRule.uses.clear()
  config.module
   .rule('svg-sprite-loader')
   .test(/\.svg$/)
   .use('svg-sprite-loader')
   .loader('svg-sprite-loader')
   .options({
    symbolId: 'icon-[name]'
   })
 }

svg component

<template>
 <svg :class="svgClass" aria-hidden="true">
  <use :xlink:href="iconName" rel="external nofollow" />
 </svg>
</template>

<script>
export default {
 name: 'SvgIcon',
 props: {
  iconClass: {
   type: String,
   required: true
  },
  className: {
   type: String,
   default: ''
  }
 },
 computed: {
  iconName() {
   return `#icon-${this.iconClass}`
  },
  svgClass() {
   if (this.className) {
    return 'svg-icon ' + this.className
   } else {
    return 'svg-icon'
   }
  }
 }
}
</script>

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

```

使用svg组件

import SvgIcon from '@/components/SvgIcon.vue'
// 设置全局组件svgIcon
Vue.component('svg-icon', SvgIcon)
const req = require.context('./assets/svg', true, /\.svg$/) // 查询文件加下面的svg文件
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req) // 全局导入svg文件

2.通用组件

级联(多选且可以选择全部)组件

安装插件 multi-cascader-base-ele

使用

import multiCascader from 'multi-cascader-base-ele'
Vue.use(multiCascader)

-- 支持选择全部

<template>
 <div>
  <MultiTestCascader v-model="selectedOptions" class="multi-cascader" :props="customProps" :options="options" multiple filterable select-children :show-all-levels="false" clearable only-out-put-leaf-node @change="cascaderChange" />
 </div>
</template>
<script>
export default {
 props: {
 // 传入级联列表数据
  options: {
   type: Array,
   default: () => []
  },
  // 传入选择数据
  list: {
   type: Array,
   default: () => []
  },
  // 自定义相关字段
  customProps: {
   type: Object,
   default: () => {
    return {
     label: 'label',
     value: 'value',
     children: 'children'
    }
   }
  },
  // 显示全部类型 1 全部二级/全部三级 2 全部二级分类/全部三级分类 3 全省/全市
  type: {
   type: String,
   default: () => '1'
  }
 },
 data() {
  return {
   selectedOptions: this.list,
   listStatus: true
  }
 },
 created() {

 },
 watch: {
  options(newValue, oldValue) {
   this.setListDisabled(newValue)
   this.addAllLabel(newValue)
  },
  list(newValue) {
   if (this.listStatus) {
    this.cascaderChange(newValue)
    this.listStatus = false
   }
  }
 },
 mounted() {
  this.setListDisabled(this.options)
  this.addAllLabel(this.options)
 },
 methods: {
  addAllLabel(list) {
   list.forEach(val => {
    if (val[this.customProps.children] && val[this.customProps.children].length > 0 && val[this.customProps.children][0][this.customProps.label] !== (this.type === '1' ? '全部一级' : (this.type === '2' ? '全部一级分类' : (this.type === '3' ? '全省' : '')))) {
     if (val[this.customProps.children].length > 1) {
      val[this.customProps.children].unshift({
       [this.customProps.label]: this.type === '1' ? '全部二级' : (this.type === '2' ? '全部二级分类' : (this.type === '3' ? '全省' : '')),
       [this.customProps.value]: val[this.customProps.value],
       [this.customProps.children]: null
      })
     }
     val[this.customProps.children].forEach(v => {
      if (v[this.customProps.children] && v[this.customProps.children].length > 1 && v[this.customProps.children][0][this.customProps.label] !== (this.type === '1' ? '全部二级' : (this.type === '2' ? '全部二级分类' : (this.type === '3' ? '全省' : '')))) {
       if (v[this.customProps.children].length > 1) {
        v[this.customProps.children].unshift({
         [this.customProps.label]: this.type === '1' ? '全部三级' : (this.type === '2' ? '全部三级分类' : (this.type === '3' ? '全市' : '')),
         [this.customProps.value]: v[this.customProps.value],
         [this.customProps.children]: null
        })
       }
      }
     })
    }
   })
  },
  setListDisabled(list) {
   const label = this.customProps.label
   const value = this.customProps.value
   const children = this.customProps.children
   list.forEach(val => {
    val.disabled = false
    if (val[children]) this.setListDisabled(val[children])
   })
  },
  cascaderChange(itemList) {
   if (!itemList || itemList.length === 0) {
    this.selectedOptions = []
   }
   this.setListDisabled(this.options)
   const label = this.customProps.label
   const value = this.customProps.value
   const children = this.customProps.children
   this.options.forEach((v, l) => {
    this.selectedOptions.forEach(val => {
     if (val[0] === '-1') {
      if (v[value] !== '-1') v.disabled = true
      else v.disabled = false
      if (v[children] && v[children].length > 0) {
       v[children].forEach(c => { c.disabled = true })
      }
     } else {
      if (v[value] === '-1') v.disabled = true
      else v.disabled = false
      if (v[children] && v[children].length > 0) {
       v[children].forEach(c => { c.disabled = false })
      }
     }
     if (val.length === 2 && v[value] === val[0] && v[children]) {
      v[children].forEach((item, num) => {
       item.disabled = false
       if (val[0] === val[1] && item[value] === val[1]) {
        item.disabled = false
       } else {
        if (val[0] === val[1] && num !== 0) {
         item.disabled = true
         if (item[children]) {
          item[children].forEach(i => {
           i.disabled = true
          })
         }
        }
        if (val[0] !== val[1] && num === 0 && v[children].length > 1) item.disabled = true
       }
       // this.options[l][children][0].disabled = true
      })
     }
     if (val.length === 3 && v[value] === val[0] && v[children]) {
      v[children].forEach((item, j) => {
       // let status = false
       if (item[children] && val[1] === item[value]) {
        item.disabled = false
        item[children].forEach((i, index) => {
         i.disabled = false
         if (i[value] === val[2]) status = true
         if (i[value] === val[2] && val[1] === val[2]) {
          i.disabled = false
         } else {
          if (val[1] !== val[2] && index === 0 && v[children].length > 1) i.disabled = true
          if (val[1] === val[2] && index !== 0) i.disabled = true
         }
        })
        // this.options[0].disabled = true
        this.options[l][children][0].disabled = true
        // return status
       }
      })
     }
    })
   })
   this.selectedOptions = this.selectedOptions.map(val => {
    if (val.length === 2 && val[0] === val[1]) return [val[0]]
    if (val.length === 1 && val[0] === '-1') return [val[0]]
    if (val.length === 3 && val[1] === val[2]) return [val[0], val[1]]
    return val
   })
   const item = this.selectedOptions[this.selectedOptions.length - 1]
   const length = this.selectedOptions.length
   let status = -1
   this.selectedOptions.some((val, index) => {
    if ((length - 1) === index) return true
    if (item.length === val.length) {
     if (item.join(',') === val.join(',')) {
      status = 1
      return true
     }
    }
    if (item.length > val.length) {
     if (item.join(',').includes(val.join(','))) {
      status = 2
      return true
     }
    }
    if (val.length > item.length) {
     if (val.join(',').includes(item.join(','))) {
      status = 3
      return true
     }
    }
   })
   if (status !== -1) {
    this.selectedOptions.splice(this.selectedOptions.length - 1, 1)
   }
   this.$emit('update:list', this.selectedOptions)
  }
 }
}
</script>

上传(支持图片/视频/裁剪图片/拖拽)

安装插件

vuedraggable axios vue-cropper

代码

<!-- -->
<template>
 <div class="image-draggable">
  <draggable v-model="draggableList" @end="onEnd">
   <!-- <transition-group> -->
   <div v-for="(item, index) in draggableList" :key="index" class="image-list">
    <template v-if="item.isImg">
     <img :src="item.displayUrl" alt="" srcset="" style="width: 148px; height: 148px;">
     <div class="icon">
      <span @click="viewImage(item.displayUrl)">
       <svg-icon icon-class="view" class="icon-size" style="margin-right: 10px;"></svg-icon>
      </span>
      <span @click="remove(index)">
       <svg-icon icon-class="delete" class="icon-size"></svg-icon>
      </span>
     </div>
    </template>
    <template v-if="!item.isImg">
     <video :src="item.displayUrl" :ref="item.id" :id="item.id" :poster="item.coverUrl" style="width: 148px; height: 148px;">
     </video>
     <div class="icon">
      <span v-if="item.isPlay" @click="play(item)" class="video-icon">
       <svg-icon icon-class="play" class="icon-size"></svg-icon>
      </span>
      <span v-if="!item.isPlay" @click="pause(item)" class="video-icon">
       <svg-icon icon-class="pause" class="icon-size"></svg-icon>
      </span>
      <span @click="fullPlay(item)" class="video-icon">
       <svg-icon icon-class="full" class="icon-size"></svg-icon>
      </span>
      <span @click="remove(index)">
       <svg-icon icon-class="delete" class="icon-size"></svg-icon>
      </span>
     </div>
    </template>
   </div>

   <!-- </transition-group> -->
  </draggable>
  <el-upload :id="uploadId" :disabled="isDiabled" :action="uploadUrl" class="image-upload" :headers="headers" :accept="accept" list-type="picture-card" :show-file-list="false" :on-preview="handlePictureCardPreview" :on-progress="handleProgress" :on-change="fileChange" :auto-upload="!isCropper" :on-remove="handleRemove" :on-success="imageSuccess" :before-upload="fileBeforeUpload">
   <i class="el-icon-plus"></i>
   <el-progress :percentage="percentage" v-if="isUpload && isLoading" :show-text="false"></el-progress>
  </el-upload>
  <el-dialog :visible.sync="dialogVisible">
   <img width="100%" :src="dialogImageUrl" alt="">
  </el-dialog>
  <el-dialog :visible.sync="modifyCropper">
   <div :style="{height: (autoCropHeight + 100) + 'px'}">
    <vueCropper ref="cropper" :img="imgSrc" :outputSize="option.size" :outputType="option.outputType" :info="true" :full="option.full" :canMove="option.canMove" :canMoveBox="option.canMoveBox" :original="option.original" :autoCrop="option.autoCrop" :autoCropHeight="autoCropHeight" :autoCropWidth="autoCropWidth" :fixedBox="option.fixedBox" @realTime="realTime" @imgLoad="imgLoad"></vueCropper>
   </div>
   <span slot="footer" class="dialog-footer">
    <el-button @click="modifyCropper = false">取 消</el-button>
    <el-button type="primary" @click="uploadCropperImage">确 定</el-button>
   </span>
  </el-dialog>
 </div>
</template>

<script>
// 拖拽
import draggable from 'vuedraggable'
// 裁剪
import { VueCropper } from 'vue-cropper'
// 上传地址
import { upload } from '@/api'
import { getToken } from '@/util/auth'
import axios from 'axios'

export default {
 name: '',
 data() {
  return {
   headers: {
    Authorization: getToken()
   },
   uploadUrl: upload,
   displayUrl: '',
   dialogImageUrl: '',
   dialogVisible: false,
   percentage: 0,
   accept: '',
   draggableList: [],
   isUpload: false,
   modifyCropper: false,
   isDiabled: false,
   cropperImage: {
   },
   uploadId: 'id' + Date.now(),
   imgSrc: '',
   option: {
    size: 0.5,
    full: true, // 输出原图比例截图 props名full
    outputType: 'png',
    canMove: true,
    original: true,
    canMoveBox: false,
    autoCrop: true,
    fixedBox: true
   }
  }
 },
 props: {
  // 已存在的文件
  fileList: {
   type: Array,
   default() {
    return [
    ]
   }
  },
  // 返回类型 Array 数组 Object 对象
  returnType: {
   type: String,
   default: 'Array'
  },
  // 自定义对象
  customObject: {
   type: Object,
   default: () => { }
  },
  // 上传的最大个数
  maxNum: {
   type: Number,
   required: true,
   default: 1
  },
  // 单位MB
  maxSize: {
   type: Number,
   default: 15
  },
  autoCropWidth: {
   type: Number,
   default: 180
  },
  autoCropHeight: {
   type: Number,
   default: 180
  },
  // 上传类型 All 图片/视频 image 图片 video视频
  acceptType: {
   type: String,
   default: 'All'
  },
  // 是否裁剪
  isCropper: {
   type: Boolean,
   default: false
  },
  // 是否显示加载条
  isLoading: {
   type: Boolean,
   default: true
  },

  outputSize: {
   type: Number,
   default: 1
  },
  outputType: {
   type: String,
   default: 'jpeg'
  }
 },
 components: {
  draggable,
  VueCropper
 },
 watch: {
  draggableList(newValue, oldValue) {
   this.getElement(this.draggableList.length)
  },
  fileList(newValue, oldValue) {
   this.draggableList = newValue
   this.initImage()
  }
 },

 computed: {},

 mounted() {
  if (this.acceptType === 'All') {
   this.accept = 'image/png, image/jpeg, image/gif, image/jpg, .mp4,.qlv,.qsv,.ogg,.flv,.avi,.wmv,.rmvb'
  }
  if (this.acceptType === 'image') {
   this.accept = 'image/png, image/jpeg, image/gif, image/jpg'
  }
  if (this.acceptType === 'video') {
   this.accept = '.mp4,.qlv,.qsv,.ogg,.flv,.avi,.wmv,.rmvb'
  }
  this.initImage()
 },
 methods: {
  // 获取五位数的随机数
  getRandom() {
   return (((Math.random() + Math.random()) * 10000) + '').substr(0, 5).replace('.', 0)
  },
  initImage() {
   const _this = this
   // console.log('file', this.fileList)
   if (this.fileList.length > 0) {
    this.draggableList = this.fileList.map(val => {
     let displayUrl = ''
     let coverUrl = ''
     let isImg = true
     const files = (val.url ? val.url : val).split(',')
     if (files.length === 3) {
      displayUrl = files[1]
      coverUrl = files[2]
      isImg = false
     } else if (files.length === 1) {
      displayUrl = (val.url ? val.url : val)
      isImg = true
     }
     const fileObj = Object.assign({}, {
      coverUrl: coverUrl,
      displayUrl: displayUrl,
      isImg: isImg,
      isPlay: true,
      name: Date.now(),
      url: (val.url ? val.url : val),
      id: val.id || Date.now() + _this.getRandom()
     })
     return fileObj
    }).filter(val => { return val.url })
   }
  },
  handleRemove(file, fileList) {
   this.getElement(fileList.length)
  },
  handlePictureCardPreview(file) {
   this.dialogImageUrl = file.url
   this.dialogVisible = true
  },
  handleProgress(event, file, fileList) {
   this.percentage = +file.percentage
  },
  fileBeforeUpload(file, event) {
   if (this.acceptType === 'image' && !file.type.includes('image/')) {
    this.$warning('请上传图片')
    return false
   }
   if (this.acceptType === 'video' && !file.type.includes('video/')) {
    this.$warning('请上传视频')
    return false
   }
   this.isUpload = true
   if (file.type.includes('image/') && (file.size > this.maxSize * 1024 * 1024)) {
    this.$warning(`请上传小于${this.maxSize}M的图片`)
    this.percentage = 0
    this.isLoading = false
    return false
   }
   if (file.type.includes('video/')) this.isDiabled = true
   if (this.isCropper) {
    return false
   }
  },
  fileChange(file, fileList) {
   if (file.percentage === 0 && this.isCropper) {
    if (file.raw.type.includes('video/')) {
     this.$warning('请上传图片')
     return
    }
    this.imgSrc = file.url
    this.modifyCropper = true
    this.cropperImage = {
     coverUrl: '',
     isImg: true,
     isPlay: true,
     name: file.name
    }
   }
  },
  // 实时预览函数
  realTime(data) {
   this.previews = data
  },
  imgLoad(data) {
  },
  // 裁剪后上传图片
  uploadCropperImage() {
   const _this = this
   this.$refs.cropper.getCropBlob((data) => {
    const config = {
     headers: {
      'Authorization': _this.headers.Authorization,
      'Content-Type': 'multipart/form-data'
     }
    }
    const formdata = new FormData()
    formdata.append('file', data)
    // this.uploadUrl 上传
    axios.post(this.uploadUrl, formdata, config).then(response => {
     _this.cropperImage = Object.assign({}, _this.cropperImage, {
      displayUrl: response.data.data,
      url: response.data.data,
      id: Date.now()
     })
     _this.draggableList.push(_this.cropperImage)
     _this.$emit('getImageList', _this.draggableList.map(val => {
      if (this.returnType === 'Array') {
       return val.url
      }
      if (this.returnType === 'Object') {
       return {
        url: val.url,
        uploadStatus: true
       }
      }
     }), _this.customObject)
     _this.modifyCropper = false
    }).catch(error => {
     console.log('err', error)
    })
   })
  },
  imageSuccess(response, file, fileList) {
   const _this = this
   try {
    this.getElement(fileList.length)
    let displayUrl = ''
    let coverUrl = ''
    let isImg = true
    const url = file.response.data || file.url
    this.isUpload = false
    const files = url.split(',')
    if (files.length === 3) {
     displayUrl = files[1]
     coverUrl = files[2]
     isImg = false
    } else if (files.length === 1) {
     displayUrl = url
     isImg = true
    }
    const id = Date.now()
    _this.draggableList.push({
     name: file.name,
     url: url,
     coverUrl: coverUrl,
     displayUrl: displayUrl,
     isImg: isImg,
     isPlay: true,
     id: id
    })
    if (isImg) {
     _this.percentage = 0
     _this.$emit('getImageList', _this.draggableList.map(val => {
      if (this.returnType === 'Array') {
       return val.url
      }
      if (this.returnType === 'Object') {
       return {
        url: val.url,
        uploadStatus: true
       }
      }
     }), _this.customObject)
     return
    }
    _this.$emit('getImageList', _this.draggableList.map(val => {
     if (this.returnType === 'Array') {
      return val.url
     }
     if (this.returnType === 'Object') {
      return {
       url: val.url,
       uploadStatus: false
      }
     }
    }), _this.customObject)
    setTimeout(() => {
     const keys = Object.keys(_this.$refs)
     const video = _this.$refs[`${keys[keys.length - 1]}`][0]
     const removeId = keys[keys.length - 1]
     const interval = setInterval(() => {
      if (video.readyState === 4) {
       const duration = video.duration
       this.isDiabled = false
       if (duration < 3 || duration > 60) {
        _this.$message.success('请上传大于三秒小于六十秒的视频')
        _this.percentage = 0
        // _this.remove(_this.draggableList.length - 1)
        _this.draggableList = _this.draggableList.filter(val => {
         return (val.id + '') !== (removeId + '')
        })
        _this.$emit('getImageList', _this.draggableList.map(val => {
         if (this.returnType === 'Array') {
          return val.url
         }
         if (this.returnType === 'Object') {
          return {
           url: val.url,
           uploadStatus: true
          }
         }
        }), _this.customObject)
        _this.getElement(_this.draggableList.length)
       }
       _this.percentage = 0
       _this.$emit('getImageList', _this.draggableList.map(val => {
        if (this.returnType === 'Array') {
         return val.url
        }
        if (this.returnType === 'Object') {
         return {
          url: val.url,
          uploadStatus: true
         }
        }
       }), _this.customObject)
       clearInterval(interval)
      }
      video.src = displayUrl
      video.poster = coverUrl
     }, 1000)
    }, 1000)
   } catch (error) {
    console.log('error', error)
   }
  },
  play(item) {
   const video = document.getElementById(item.id)
   video.play()
   item.isPlay = !item.isPlay
  },
  pause(item) {
   const video = document.getElementById(item.id)
   video.pause()
   item.isPlay = !item.isPlay
  },
  // 全屏播放
  fullPlay(item) {
   const video = document.getElementById(item.id)
   // w3c
   typeof video.requestFullScreen === 'function' && video.requestFullScreen()
   // webkit(谷歌)
   typeof video.webkitRequestFullScreen === 'function' && video.webkitRequestFullScreen()
   // 火狐
   typeof video.mozRequestFullScreen === 'function' && video.mozRequestFullScreen()
   // IE
   typeof video.msExitFullscreen === 'function' && video.msExitFullscreen()
  },
  viewImage(url) {
   this.dialogImageUrl = url
   this.dialogVisible = true
  },
  remove(index) {
   this.draggableList.splice(index, 1)
   this.$emit('getImageList', this.draggableList.map(val => {
    if (this.returnType === 'Array') {
     return val.url
    }
    if (this.returnType === 'Object') {
     return {
      url: val.url,
      uploadStatus: true
     }
    }
   }), this.customObject)
   this.getElement(this.draggableList.length)
  },
  onEnd(event) {
   this.$emit('getImageList', this.draggableList.map(val => {
    if (this.returnType === 'Array') {
     return val.url
    }
    if (this.returnType === 'Object') {
     return {
      url: val.url,
      uploadStatus: true
     }
    }
   }), this.customObject)
  },
  isImg(obj) {
   const item = obj.url
   if (item === '' || item === null || typeof item === 'undefined') {
    return false
   }
   const index = item.lastIndexOf('.')
   var ext = item.substr(index + 1)
   if (ext.includes('!')) ext = ext.split('!')[0]
   ext = ext.toLowerCase()
   var tps = ['jpg', 'jpeg', 'png']
   let ok = false
   for (let i = 0; i < tps.length; i++) {
    if (tps[i] === ext) {
     ok = true
     break
    }
   }
   return ok
  },
  getElement(length) {
   const _this = this
   if (length >= _this.maxNum) {
    document.querySelectorAll(`#${_this.uploadId} .el-upload--picture-card`).forEach(val => {
     if (val.firstElementChild.className === 'el-icon-plus') {
      val.style.display = 'none'
      return true
     }
    })
   } else {
    document.querySelectorAll(`#${_this.uploadId} .el-upload--picture-card`).forEach(val => {
     if (val.firstElementChild.className === 'el-icon-plus') {
      val.style.display = 'inline-block'
      return true
     }
    })
   }
  }
 }
}

</script>
<style lang='scss' scoped>
.image-draggable {
  display: flex;
  flex-wrap: wrap;
  .image-list {
    position: relative;

    display: inline-block;
    overflow: hidden;

    width: 148px;
    height: 148px;
    margin-right: 10px;

    cursor: pointer;
    &:hover {
      .icon {
        height: 20%;

        transition: all .5s;
        .video-icon {
          display: inline-block;

          margin-right: 10px;
        }
      }
    }
    .icon {
      position: absolute;
      bottom: 0;

      display: flex;
      justify-content: center;

      width: 100%;
      height: 0;

      background-color: rgba(215, 215, 215, 1);
      .icon-size {
        width: 2em;
        height: 2em;
      }
      .video-icon {
        display: none;
      }
    }
  }
}
</style>
<style lang="scss">
.image-draggable {
  .el-progress {
    top: -50%;
  }
}
</style>

注册全局事件

创建eventBus.js

vue3.0 搭建项目总结(详细步骤)

使用

import eventBus from './plugins/eventBus'
Vue.use(eventBus)

处理缓存

借用mounted, activated 事件处理数据

在某一次打开页面的时候进行数据初始化存储, 放置在vuex中,或者全局变量中,当需要初始化进行一个初始化,采取mixins引入

vue3.0 搭建项目总结(详细步骤)

vue3.0 搭建项目总结(详细步骤)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 目录列举函数
Nov 06 Javascript
extjs 学习笔记 四 带分页的grid
Oct 20 Javascript
js下将字符串当函数执行的方法
Jul 13 Javascript
jQuery遍历json中多个map的方法
Feb 12 Javascript
JavaScript字符串常用的方法
Mar 10 Javascript
Bootstrap DateTime Picker日历控件简单应用
Mar 25 Javascript
Vue.js鼠标悬浮更换图片功能
May 17 Javascript
浅谈vue引入css,less遇到的坑和解决方法
Jan 20 Javascript
vue 2.0 购物车小球抛物线的示例代码
Feb 01 Javascript
JS实现百度网盘任意文件强制下载功能
Aug 31 Javascript
微信小程序实现授权登录
May 15 Javascript
vue中解决拖拽改变存在iframe的div大小时卡顿问题
Jul 22 Javascript
vue-cli webpack配置文件分析
May 20 #Javascript
微信小程序开发之左右分栏效果的实例代码
May 20 #Javascript
微信小程序rich-text富文本用法实例分析
May 20 #Javascript
bootstrap中的导航条实例代码详解
May 20 #Javascript
详解小程序云开发数据库
May 20 #Javascript
VUE脚手架具体使用方法
May 20 #Javascript
Vue CLI2升级至Vue CLI3的方法步骤
May 20 #Javascript
You might like
PHP 源代码压缩小工具
2009/12/22 PHP
php简单实现数组分页的方法
2016/04/30 PHP
php从身份证获取性别和出生年月
2017/02/09 PHP
PHP levenshtein()函数用法讲解
2019/03/08 PHP
Laravel6.18.19如何优雅的切换发件账户
2020/06/14 PHP
关于js类的定义
2011/06/28 Javascript
javascript的解析执行顺序在各个浏览器中的不同
2014/03/17 Javascript
javascript制作sql转换为stringBuffer的小工具
2015/04/03 Javascript
JavaScript操作表单实例讲解(上)
2016/06/20 Javascript
AngularJS入门教程之更多模板详解
2016/08/19 Javascript
jQuery实现checkbox列表的全选、反选功能
2016/11/24 Javascript
基于node.js实现爬虫的讲解
2019/02/18 Javascript
JS定义函数的几种常用方法小结
2019/05/23 Javascript
[03:04]DOTA2超级联赛专访ZSMJ “莫名其妙”的逆袭
2013/05/23 DOTA
在Linux上安装Python的Flask框架和创建第一个app实例的教程
2015/03/30 Python
Python中字符串格式化str.format的详细介绍
2017/02/17 Python
python Socket之客户端和服务端握手详解
2017/09/18 Python
selenium python浏览器多窗口处理代码示例
2018/01/15 Python
python3利用ctypes传入一个字符串类型的列表方法
2019/02/12 Python
Python迭代器模块itertools使用原理解析
2019/12/11 Python
PYQT5开启多个线程和窗口,多线程与多窗口的交互实例
2019/12/13 Python
Python 实现将数组/矩阵转换成Image类
2020/01/09 Python
Python必须了解的35个关键词
2020/07/16 Python
Html5插件教程之添加浏览器放大镜效果的商品橱窗
2016/01/07 HTML / CSS
乐高奥地利官方商店:LEGO Shop AT
2019/07/16 全球购物
一些Solaris面试题
2013/03/22 面试题
Java程序开发中如何应用线程
2016/03/03 面试题
幼儿教师个人求职信范文
2013/09/21 职场文书
关于期中考试的反思
2014/02/02 职场文书
《最后的姿势》教学反思
2014/02/27 职场文书
电气工程自动化求职信
2014/03/14 职场文书
记账会计岗位职责
2014/06/16 职场文书
党员四风问题对照检查材料思想汇报
2014/09/16 职场文书
仓管员岗位职责
2015/02/03 职场文书
国庆阅兵观后感
2015/06/15 职场文书
行政处罚听证告知书
2015/07/01 职场文书