幻影

幻影 查看完整档案

丽江编辑广东舞蹈戏剧职业学院  |  我是歌手 编辑我心即我在  |  无名 编辑 zhl.zunglei.com/ 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

幻影 提出了问题 · 8月17日

vue动态添加指令和事件无效

image.png

image.png

一段文本内容,找到关键字添加点击事件,点击无法触发?
怎么肥事??

关注 1 回答 1

幻影 赞了回答 · 7月24日

解决position absolute 怎么可以跟着页面滚动!

你给父元素设置position:relative没有用吗?

关注 3 回答 2

幻影 赞了文章 · 3月13日

基于vue-cli3.0构建功能完善的移动端架子

基于vue-cli3.0构建功能完善的移动端架子,主要功能包括

  1. webpack 打包扩展
  2. css:sass支持、normalize.css、_mixin.scss、_variables.scss
  3. vw、rem布局
  4. 跨域设置
  5. eslint设置
  6. cdn引入
  7. 路由设计、登录拦截
  8. axios、api 设计
  9. vuex状态管理
项目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

webpack 打包扩展

vue-cli3.*后目录结构大改,去除了以往的build,config文件夹,要实现配置的改动在根目录下增加vue.config.js进行配置

css:sass支持、normalize.css、_mixin.scss、_variables.scss

使用的css预处理器是sass,对于css mixin,变量这里做了全局引入,并且引入normalize.css 使HTML元素样式在跨浏览器上表现得的高度一致性
vue.config.js配置

css: {
        // 是否使用css分离插件 ExtractTextPlugin
        extract:isProduction ? true:false,
        // 开启 CSS source maps?
        sourceMap: false,
        // css预设器配置项
        // 启用 CSS modules for all css / pre-processor files.
        modules: false,
            sass: {
                data: '@import "style/_mixin.scss";@import "style/_variables.scss";' // 全局引入
            }
        }
    }

vw、rem布局

对于移动端适配方案使用的是网易新闻的方法,
使用vw + rem布局

/**
750px设计稿
    取1rem=100px为参照,那么html元素的宽度就可以设置为width: 7.5rem,于是html的font-size=deviceWidth / 7.5
**/
html {
    font-size: 13.33333vw
}

@media screen and (max-width: 320px) {
    html {
        font-size: 42.667PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 321px) and (max-width:360px) {
    html {
        font-size: 48PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 361px) and (max-width:375px) {
    html {
        font-size: 50PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 376px) and (max-width:393px) {
    html {
        font-size: 52.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 394px) and (max-width:412px) {
    html {
        font-size: 54.93PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 413px) and (max-width:414px) {
    html {
        font-size: 55.2PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 415px) and (max-width:480px) {
    html {
        font-size: 64PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 481px) and (max-width:540px) {
    html {
        font-size: 72PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 541px) and (max-width:640px) {
    html {
        font-size: 85.33PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 641px) and (max-width:720px) {
    html {
        font-size: 96PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 721px) and (max-width:768px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;

        #app {
            margin: 0 auto
        }
    }


}

vue.config.js配置

loaderOptions: {
    postcss: {
        // 这是rem适配的配置
        plugins: [
            require('postcss-px2rem')({
                remUnit: 100
            })
        ]
    }
}

开发时跨域设置

devServer: {
        open: true, // 启动服务后是否打开浏览器
        host: '127.0.0.1',
        port: 8088, // 服务端口
        https: false,
        hotOnly: false,
        proxy: 'https://easy-mock.com/' // 设置代理
    }

配置完后,本地开发环境的axios的baseUrl要写为 '' ,即空字符串。
发布到线上时如果前端代码不是和后台api放在同源下的,后台还需做跨域处理,

eslint standard设置

使用的是JavaScript standard 代码规范,一个好的编码风格它可以帮助减少团队之间的摩擦,代码阅读起来也更加清爽,更加可读性,不要觉得烦,用了都说好。
这是 JavaScript standard 代码规范的全文

自定义配置,在.eslintrc.js里修改,这里是我给出的配置,4个空格缩进,不检查结尾分号,关闭单var 声明,可自行配置

rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    indent: [
        'error',
        4,
        {
            SwitchCase: 1
        }
    ],
    semi: 0, // 不检查结尾分号,
    // 强制使用单引号
    quotes: ['error', 'single'],
    // 关闭函数名与后面括号间必须空格规则
    'space-before-function-paren': 0,
    // 关闭var 声明,每个声明占一行规则。
    'one-var': 0
    }

cdn引入

对于 vue、vue-router、vuex、axios等等这些不经常改动的库、我们让webpack不对他们进行打包,通过cdn引入,可以减少代码的大小、也可以减少服务器的带宽
这里使用的是360的cdn,附上一份公共cdn评测文章 点我

vue.config.js配置

const externals = {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    'mint-ui': 'MINT',
    axios: 'axios'

}

const cdn = {
    // 开发环境
    dev: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: []
    },
    // 生产环境
    build: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: [
            'https://lib.baomitu.com/vue/2.6.6/vue.min.js',
            'https://lib.baomitu.com/vue-router/3.0.1/vue-router.min.js',
            'https://lib.baomitu.com/vuex/3.0.1/vuex.min.js',
            'https://lib.baomitu.com/axios/0.18.0/axios.min.js',
            'https://lib.baomitu.com/mint-ui/2.2.13/index.js'
        ]
    }
}

configureWebpack: config => {
        if (isProduction) {
            // externals里的模块不打包
            Object.assign(config, {
                externals: externals
            })
       
        } else {
            // 为开发环境修改配置...
        }
    },
chainWebpack: config => {
    // 对vue-cli内部的 webpack 配置进行更细粒度的修改。
    // 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
    config.plugin('html').tap(args => {
        if (process.env.NODE_ENV === 'production') {
            args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === 'development') {
            args[0].cdn = cdn.dev
        }
        return args
    })
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!-- DNS预解析 -->
    <link rel="dns-prefetch" href="//lib.baomitu.com" />
    <meta name="viewport"
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0,minimal-ui,viewport-fit=cover" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
    <% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
    <% } %>

    <title>vuedemo</title>
</head>

<body>
    <noscript>
        <strong>We're sorry but vuedemo doesn't work properly without JavaScript
            enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
    <% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
    <script data-original="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>

    <!-- built files will be auto injected -->
</body>

</html>

路由设计、登录拦截

const router = new Router({
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home,
            meta: {
                auth: false, // 是否需要登录
                keepAlive: true // 是否缓存组件
            }
        },
        {
            path: '/about',
            name: 'about',
            component: () =>
                import(/* webpackChunkName: "about" */ './views/About.vue'),
            meta: {
                auth: true,
                keepAlive: true
            }
        },
        {
            path: '/login',
            name: 'login',
            component: () =>
                import(/* webpackChunkName: "login" */ './views/login.vue'),
            meta: {
                auth: false,
                keepAlive: true
            }
        },
        {
            path: '*', // 未匹配到路由时重定向
            redirect: '/',
            meta: {
                // auth: true,
                // keepAlive: true
            }
        }
    ]
})

// 全局路由钩子函数 对全局有效
router.beforeEach((to, from, next) => {
    let auth = to.meta.auth
    let token = store.getters['login/token'];

    if (auth) { // 需要登录
        if (token) {
            next()
        } else {
            next({
                path: '/login',
                query: {
                    redirect: to.fullPath
                }
            })
        }
    } else {
        next()
    }
})

在meta中设置是否需要登录以及是否缓存当前组件,
在router.beforeEac路由钩子函数中对登录权限判断,没有登录的跳到登录页面,并且把当前页面传过去,登录后跳回这个页面。

对于页面缓存的在app.vue里进行处理

<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

axios、api 设计

对于axios的设计主要是请求拦截器, respone拦截器,以及get,post的二次封装

axios.defaults.timeout = 12000 // 请求超时时间
axios.defaults.baseURL = process.env.VUE_APP_BASE_API

axios.defaults.headers.post['Content-Type'] =
    'application/x-www-form-urlencoded;charset=UTF-8' // post请求头的设置
// axios 请求拦截器
axios.interceptors.request.use(
    config => {
        // 可在此设置要发送的token
        let token = store.getters['login/token'];
        token && (config.headers.token = token)
        Indicator.open('数据加载中')
        return config
    },
    error => {
        return Promise.error(error)
    }
)
// axios respone拦截器
axios.interceptors.response.use(
    response => {
        // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
        // 否则的话抛出错误 结合自身业务和后台返回的接口状态约定写respone拦截器
        Indicator.close()
        if (response.status === 200 && response.data.code === 0) {
            return Promise.resolve(response)
        } else {
            Toast({
                message: response.data.msg,
                position: 'middle',
                duration: 2000
            });
            return Promise.reject(response)
        }
    },
    error => {
        Indicator.close()
        const responseCode = error.response.status
        switch (responseCode) {
            // 401:未登录
            case 401:
                break
            // 404请求不存在
            case 404:
                Toast({
                    message: '网络请求不存在',
                    position: 'middle',
                    duration: 2000
                });
                break
            default:
                Toast({
                    message: error.response.data.message,
                    position: 'middle',
                    duration: 2000
                });
        }
        return Promise.reject(error.response)
    }
)
/**
 * 封装get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
function get (url, params = {}) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, {
                params: params
            })
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
    // 或者return axios.get();
}
/**
 * post方法,对应post请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
function post (url, params) {
    return new Promise((resolve, reject) => {
        axios
            .post(url, qs.stringify(params))
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
    //  或者return axios.post();
}

为了方便管理api路径,这里把所以请求都放在了api文件夹下,如

import { get, post } from '@/axios/http.js'
function getIndex (params) {
    return get('/mock/5cb48c7ed491cd741c54456f/base/index', params)
}
function login(params) {
    return post('/mock/5cb48c7ed491cd741c54456f/base/login', params)
}
export {
    getIndex,
    login
}

其他

去除console.log

装uglifyjs-webpack-plugin插件

 // 上线压缩去除console等信息
config.plugins.push(
    new UglifyJsPlugin({
        uglifyOptions: {
            compress: {
                warnings: false,
                drop_console: true,
                drop_debugger: false,
                pure_funcs: ['console.log'] // 移除console
            }
        },
        sourceMap: false,
        parallel: true
    })
)

设置alias目录别名

在项目中经常会引用各个地方的文件,配置后可以更加方便的引入了

config.resolve.alias
            .set('assets', '@/assets')
            .set('components', '@/components')
            .set('view', '@/view')
            .set('style', '@/style')
            .set('api', '@/api')
            .set('store', '@/store')

环境变量和模式

在一个产品的前端开发过程中,一般来说会经历本地开发、测试脚本、开发自测、测试环境、预上线环境,然后才能正式的发布。对应每一个环境可能都会有所差异,比如说服务器地址、接口地址、websorket地址…… 等等。在各个环境切换的时候,就需要不同的配置参数,所以就可以用环境变量和模式,来方便我们管理。

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

自定义的变量VUE_APP_开头,两个特殊的变量:

  1. NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。具体的值取决于应用运行的模式。
  2. BASE_URL - 会和 vue.config.js 中的 baseUrl 选项相符,即你的应用会部署到的基础路径。

如我们定义的.env

NODE_ENV = 'development'
BASE_URL = '/'
VUE_APP_BASE_API = ''

.env.production

NODE_ENV = 'production'
BASE_URL = './'
VUE_APP_BASE_API = 'https://easy-mock.com/'

在项目中可以用process.env.VUE_APP_*,如process.env.VUE_APP_BASE_API获取到定义的值

全局引入filter

把多个地方用到的过滤器写在一个js里面,复用代码。

// 过滤日期格式,传入时间戳,根据参数返回不同格式
const formatTimer = function(val, hours) {
    if (val) {
        var dateTimer = new Date(val * 1000)
        var y = dateTimer.getFullYear()
        var M = dateTimer.getMonth() + 1
        var d = dateTimer.getDate()
        var h = dateTimer.getHours()
        var m = dateTimer.getMinutes()
        M = M >= 10 ? M : '0' + M
        d = d >= 10 ? d : '0' + d
        h = h >= 10 ? h : '0' + h
        m = m >= 10 ? m : '0' + m
        if (hours) {
            return y + '-' + M + '-' + d + ' ' + h + ':' + m
        } else {
            return y + '-' + M + '-' + d
        }
    }
}
export default {
    formatTimer
}

main.js引入

import filters from './filters/index'
// 注入全局过滤器
Object.keys(filters).forEach(item => {
    Vue.filter(item, filters[item])
})

使用

{{1555851774 | formatTimer()}}

vue中使用mock.js

查看我以前写的文章点击我

wepback的可视化资源分析工具插件---webpack-bundle-analyzer

用来分析哪些模块引入了哪些代码,进行有目的性的优化代码

在打包环境中加,使用命令npm run build --report

if (process.env.npm_config_report) {
    config.plugins.push(new BundleAnalyzerPlugin())
}

111

代码地址

项目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

没有服务器学习的童鞋可以看看腾讯云的 1核2G,1M带宽,50GB存储空间的云服务器,120一年哦。

查看原文

赞 180 收藏 146 评论 7

幻影 关注了标签 · 3月13日

前端

Web前端开发是从网页制作演变而来的,名称上有很明显的时代特征。在互联网的演化进程中,网页制作是Web 1.0时代的产物,那时网站的主要内容都是静态的,用户使用网站的行为也以浏览为主。2005年以后,互联网进入Web 2.0时代,各种类似桌面软件的Web应用大量涌现,网站的前端由此发生了翻天覆地的变化。网页不再只是承载单一的文字和图片,各种富媒体让网页的内容更加生动,网页上软件化的交互形式为用户提供了更好的使用体验,这些都是基于前端技术实现的。

Web前端优化
  1. 尽量减少HTTP请求 (Make Fewer HTTP Requests)
  2. 减少 DNS 查找 (Reduce DNS Lookups)
  3. 避免重定向 (Avoid Redirects)
  4. 使得 Ajax 可缓存 (Make Ajax Cacheable)
  5. 延迟载入组件 (Post-load Components)
  6. 预载入组件 (Preload Components)
  7. 减少 DOM 元素数量 (Reduce the Number of DOM Elements)
  8. 切分组件到多个域 (Split Components Across Domains)
  9. 最小化 iframe 的数量 (Minimize the Number of iframes)
  10. 杜绝 http 404 错误 (No 404s)

关注 152264

幻影 关注了标签 · 3月13日

css3

层叠样式表(英语:Cascading Style Sheets,简写CSS),又称串样式列表,由W3C定义和维护的标准,一种用来为结构化文档(如HTML文档或XML应用)添加样式(字体、间距和颜色等)的计算机语言。目前最新版本是CSS2.1,为W3C的候选推荐标准。CSS3现在已被大部分现代浏览器支持,而下一版的CSS4仍在开发过程中。

关注 23237

幻影 关注了标签 · 3月13日

css

层叠样式表(英语:Cascading Style Sheets,简写CSS),又称串样式列表,由W3C定义和维护的标准,一种用来为结构化文档(如HTML文档或XML应用)添加样式(字体、间距和颜色等)的计算机语言。

关注 61528

幻影 关注了标签 · 3月13日

html

超文本标记语言(英文:HyperText Markup Language,HTML)是为“网页创建和其它可在网页浏览器中看到的信息”设计的一种标记语言。

关注 62270

幻影 提出了问题 · 3月7日

移动端H5界面打开后,如何自动调用软键盘

image.png

          test(){
             // let aa = this.$refs.input1.blur();
              // this.$nextTick((x)=>{   //正确写法
              //       // this.$refs.inputs.focus();
                    // // console.log(x)
                    // // this.$refs.inp[0].$refs.input.focus();
              //    },3000)
                 // $api.dom('input').focus();
                 
                // this.$refs.Inp.focus();
             
              // aa.focus();
              // $("input").trigger("click").focus();
            $api.dom('input').addEventListener("touchstart", function(e){
                console.log("aaaaaaa");
                $("input").trigger("click").focus();
            })
            let theEvent = document.createEvent("Events");
            theEvent.initEvent("touchstart", true, true);  
            $api.dom('input').dispatchEvent(theEvent);
              
             console.log(88888)
             console.log(this.focusVal)
        },

如图,移动端H5界面打开后,如何自动调用软键盘,试了很多方法,发现只能聚焦,并不能调起软键盘。。。。
是不是h5没办法做到?
大佬们,帮忙看下

关注 2 回答 1

幻影 赞了回答 · 1月7日

解决vue 短信验证码倒计时

这跟jquery的也没区别啊……
button加个事件:

<x-button slot="right" type="primary" action-type="button" mini @click="sendMessage">{{word}}</x-button>

加两个属性:

data(){
    return {
       word: '发送验证码',
       isOvertime: false
    }
},
methods: {
    sendMessage(){
        if(this.isOvertime){
            return false;
        }
        let that = this,
            time = 60;
        var sendTimer = setInterval(function(){
            that.isOvertime = true;
            time--;
            that.word = "重新发送"+time;
            if(time < 0){
                that.isOvertime = false;
                clearInterval(sendTimer);
                that.word = "获取验证码";
            }
        },1000)
    }
}

关注 3 回答 3

幻影 关注了用户 · 2019-12-23

顾昀峰 @gufeng_5cfe02f0b59c6

关注 3

幻影 赞了回答 · 2019-12-22

解决html 引入vue,vant/ list列表组件,下拉没反应

你没有把vant-list注册到vue中,我忘记了还有一个组件需要搭配一起实用
image.png
https://youzan.github.io/vant/#/zh-CN/pull-refresh

关注 2 回答 1

幻影 关注了标签 · 2019-12-22

vant

Vant 是一套由有赞技术团队精心打造的轻量可靠的移动组件库,已经过50+个有赞线上业务的检验。

关注 13

幻影 提出了问题 · 2019-12-21

解决html 引入vue,vant/ list列表组件,下拉没反应

如图,
image.png

代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.2/lib/index.css">
        <style type="text/css">
            #app {
                
            }
            .test{
                width: 100%;
                height: 100px;
                line-height: 100px;
                text-align: center;
                font-size: 33px;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <van-list
              v-model="loading"
              :finished="finished"
              finished-text="没有更多了"
              @load="onLoad"
            >
              <div class="test" v-for="item in list"
                :key="item"
                :title="item">
                  {{item}}
              </div>
            </van-list>
        </div>
    </body>
</html>
<script data-original="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script data-original="https://cdn.jsdelivr.net/npm/vant@2.2/lib/vant.min.js"></script>
<script type="text/javascript">
    

    let vm = new Vue({
        el:'#app',
        data:{
            count: 0,
            isLoading: false,
            list: [],
                  loading: false,
                  finished: false
        },
        methods:{
            onLoad() {
                  // 异步更新数据
                  setTimeout(() => {
                    for (let i = 0; i < 16; i++) {
                      this.list.push(this.list.length + 1);
                    }
                    // 加载状态结束
                    this.loading = false;

                    // 数据全部加载完成
                    if (this.list.length >= 60) {
                      this.finished = true;
                    }
                  }, 500);
                },
        }
    });

</script>

html 引入vue,vant/ list列表组件,下拉没反应,上拉有效果。。。
各位大佬,这是怎么回事呢,提供下写法>_<

关注 2 回答 1

幻影 赞了回答 · 2019-12-19

解决html 使用vant轮播组件没效果

有两处错误
1.1576722504977.jpg
2.1576722547994.jpg
效果1576722614227.jpg


<style>
 img{
     width: 100%;

     height: 300px;
 }
</style>

关注 2 回答 1

幻影 提出了问题 · 2019-12-19

解决html 使用vant轮播组件没效果

如图所示
image.png

代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" />
        <meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
        <title>找房</title>
        <!-- <link rel="stylesheet" type="text/css" href="../css/api.css" />
        <link rel="stylesheet" type="text/css" href="../css/style.css" />
        <link rel="stylesheet" type="text/css" href="../css/aui.css" /> -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.2/lib/index.css">
        <style>
            
    </style>
    </head>
    <body>
        <div id='app'>
            
            <div>
                <van-swipe :autoplay="3000">
                  <van-swipe-item v-for="(image, index) in images" :key="index">
                    <img data-original="image" />
                  </van-swipe-item>
                </van-swipe>

        </div>
    </body>
</html>
<script data-original="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script data-original="https://cdn.jsdelivr.net/npm/vant@2.2/lib/vant.min.js"></script>
<script type="text/javascript">
    let vm = new Vue({
        el: "#app",
        data: {
            
             images: [
                    'https://img.yzcdn.cn/vant/apple-1.jpg',
                    'https://img.yzcdn.cn/vant/apple-2.jpg',
                    'https://img.yzcdn.cn/vant/apple-3.jpg',
                    'https://img.yzcdn.cn/vant/apple-4.jpg',
                  ]
        },
        mounted() {
            
        },
        methods: {
            
    });
</script>

各位大佬,这是怎么回事,少了什么配置吗

关注 2 回答 1

幻影 提出了问题 · 2019-07-17

react 引用腾讯地图-地图选点组件报错 Cannot read property 'formatted_addresses'

react 引用腾讯地图-地图选点组件报错
Uncaught TypeError: Cannot read property 'formatted_addresses' of undefined

怎么解决?

关注 3 回答 2

幻影 提出了问题 · 2019-06-12

html2canvas-jspdf 生成pdf显示不全

图片描述

     html2canvas(element).then(canvas => {
        const imgWidth = 595.28;
        const imgHeight = 592.28/canvas.width * canvas.height;
        const pdf = new jsPDF('', 'pt', 'a4'); // A4 size page of PDF

        const contentDataURL = canvas.toDataURL('image/jpeg',1.0);
        pdf.addImage(contentDataURL, 'JPEG', 0, 0, imgWidth, imgHeight);
        pdf.save(Date.now()+'.pdf');
    });
    
    
    
    
    

html2canvas-jspdf 生成pdf显示不全? 没有用到css
这是怎么回事?

关注 1 回答 0

幻影 赞了文章 · 2019-06-11

Javascript 将html转成pdf,下载,支持多页哦(html2canvas 和 jsPDF)

最近碰到个需求,需要把当前页面生成pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

项目源码地址https://github.com/linwalker/...

html2canvas

简介

我们可以直接在浏览器端使用html2canvas,对整个或局部页面进行‘截图’。但这并不是真的截图,而是通过遍历页面DOM结构,收集所有元素信息及相应样式,渲染出canvas image。

由于html2canvas只能将它能处理的生成canvas image,因此渲染出来的结果并不是100%与原来一致。但它不需要服务器参与,整个图片都由客户端浏览器生成,使用很方便。

使用

使用的API也很简洁,下面代码可以将某个元素渲染成canvas:

html2canvas(element, {
    onrendered: function(canvas) {
        // canvas is the final rendered <canvas> element
    }
});

通过onrendered方法,可以将生成的canvas进行回调,比如插入到页面中:

html2canvas(element, {
    onrendered: function(canvas) {
       document.body.appendChild(canvas);
    }
});

做个小例子代码如下,在线展示链接demo1

<html>
  <head>
    <title>html2canvas example</title>
    <style type="text/css">...</style>
  </head>
  <body>
    <header>
      <nav>
        <ul>
          <li>one</li>
          ...
        </ul>
      </nav>
    </header>
    <section>
      <aside>
        <h3>it is a title</h3>
        <a href="">Stone Giant</a>
        ...
     </aside>
      <article>
        <img data-original="./Stone.png">
        <h2>Stone Giant</h2>
        <p>Coming ... </p>
        <p>以一团石头...</p>
      </article>
    </section>
    <footer>write by linwalker @2017</footer>
    <script type="text/javascript" data-original="./html2canvas.js"></script>
    <script type="text/javascript">
        html2canvas(document.body, {
          onrendered:function(canvas) {
            document.body.appendChild(canvas)
          }
        })
    </script>
  </body>
</html>

这个例子将页面body中的元素渲染成canvas,并插入到body中

jsPDF

jsPDF库可以用于浏览器端生成PDF。

文字生成PDF

使用方法如下:

// 默认a4大小,竖直方向,mm单位的PDF
var doc = new jsPDF();

// 添加文本‘Download PDF’
doc.text('Download PDF!', 10, 10);
doc.save('a4.pdf');

在线演示demo2

图片生成PDF

使用方法如下:

// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('landscape','pt',[205, 115])

// 将图片转化为dataUrl
var imageData = ‘data:image/png;base64,iVBORw0KGgo...’;

doc.addImage(imageData, 'PNG', 0, 0, 205, 115);
doc.save('a4.pdf');

在线演示demo3

文字与图片生成PDF

// 三个参数,第一个方向,第二个尺寸,第三个尺寸格式
var doc = new jsPDF('landscape','pt',[205, 155])

// 将图片转化为dataUrl
var imageData = ‘data:image/png;base64,iVBORw0KGgo...’;

//设置字体大小
doc.setFontSize(20);

//10,20这两参数控制文字距离左边,与上边的距离
doc.text('Stone', 10, 20);

// 0, 40, 控制文字距离左边,与上边的距离
doc.addImage(imageData, 'PNG', 0, 40, 205, 115);
doc.save('a4.pdf')

在线演示demo4

生成pdf需要把转化的元素添加到jsPDF实例中,也有添加html的功能,但某些元素无法生成在pdf中,因此可以使用html2canvas + jsPDF的方式将页面转成pdf。通过html2canvas将遍历页面元素,并渲染生成canvas,然后将canvas图片格式添加到jsPDF实例,生成pdf。

html2canvas + jsPDF

单页

将demo1的例子修改下:

<script type="text/javascript" data-original="./js/jsPdf.debug.js"></script>
<script type="text/javascript">
      var downPdf = document.getElementById("renderPdf");
      downPdf.onclick = function() {
          html2canvas(document.body, {
              onrendered:function(canvas) {

                  //返回图片dataURL,参数:图片格式和清晰度(0-1)
                  var pageData = canvas.toDataURL('image/jpeg', 1.0);

                  //方向默认竖直,尺寸ponits,格式a4[595.28,841.89]
                  var pdf = new jsPDF('', 'pt', 'a4');

                  //addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩
                  pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height );

                  pdf.save('stone.pdf');

              }
          })
      }
</script>

在线演示demo5

如果页面内容根据a4比例转化后高度超过a4纸高度呢,生成的pdf会怎么样?会分页吗?

你可以试试,验证一下自己的想法: demo6

jsPDF提供了一个很有用的API,addPage(),我们可以通过pdf.addPage(),来添加一页pdf,然后通过pdf.addImage(...),将图片赋予这页pdf来显示。

那么我们如何确定哪里分页?

这个问题好回答,我们可以设置一个pageHeight,超过这个高度的内容放入下一页pdf。

来捋一下思路,将html页面内容生成canvas图片,通过addImage将第一页图片添加到pdf中,超过一页内容,通过addPage()添加pdf页数,然后再通过addImage将下一页图片添加到pdf中。

嗯~,很好!巴特,难道没有发现问题吗?

这个方法实现的前提是 — — 我们能根据pageHeight先将整页内容生成的canvas图片分割成对应的小图片,然后一个萝卜一个坑,一页一页addImage进去。

What? 想一想我们的canvas是肿么来的,不用拉上去,直接看下面:

html2canvas(document.body, {
    onrendered:function(canvas) {
     //it is here we handle the canvas
    }
})

这里的body就是要生成canvas的元素对象,一个元素生成一个canvas;那么我们需要一页一页的canvas,也就是说。。。

你觉得可能吗? 我觉得不太现实,按这思路要获取页面上不同位置的DOM元素,然后通过htnl2canvas(element,option)来处理,先不说能不能刚好在每个pageHeight的位置刚好找到一个DOM元素,就算找到了,这样做累不累。

累的话
:)可以看看下面这种方法

多页

我提供的思路是我们只生成一个canvas,对就一个,转化元素就是你要转成pdf内容的母元素,在这篇demo里就是body了;其他不变,也是超过一页内容就addPage,然后addImage,只不过这里添加的是同一个canvas。

当然这样做只会出现多页重复的pdf,那到底怎么实现正确分页显示。其实主要利用了jsPDF的两点:

- 超过jsPDF实例格式尺寸的内容不显示
(var pdf = new jsPDF('', 'pt', 'a4'); demo中就是a4纸的尺寸)
- addImage有两个参数可以控制图片在pdf中的位置

虽然每一页pdf上显示的图片是相同的,但我们通过调整图片的位置,产生了分页的错觉。以第二页为例,将竖直方向上的偏移设置为-841.89即一张a4纸的高度,又因为超过a4纸高度范围的图片不显示,所以第二页显示了图片竖直方向上[841.89,1682.78]范围内的内容,这就得到了分页的效果,以此类推。

还是看代码吧:

html2canvas(document.body, {
  onrendered:function(canvas) {

      var contentWidth = canvas.width;
      var contentHeight = canvas.height;

      //一页pdf显示html页面生成的canvas高度;
      var pageHeight = contentWidth / 592.28 * 841.89;
      //未生成pdf的html页面高度
      var leftHeight = contentHeight;
      //页面偏移
      var position = 0;
      //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
      var imgWidth = 595.28;
      var imgHeight = 592.28/contentWidth * contentHeight;

      var pageData = canvas.toDataURL('image/jpeg', 1.0);

      var pdf = new jsPDF('', 'pt', 'a4');

      //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
      //当内容未超过pdf一页显示的范围,无需分页
      if (leftHeight < pageHeight) {
      pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
      } else {
          while(leftHeight > 0) {
              pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
              leftHeight -= pageHeight;
              position -= 841.89;
              //避免添加空白页
              if(leftHeight > 0) {
                pdf.addPage();
              }
          }
      }

      pdf.save('content.pdf');
  }
})

在线演示demo7

两边留边距

修改imgWidth,并且在addImage时x方向参数设置你要的边距,具体代码如下

var imgWidth = 555.28;
var imgHeight = 555.28/contentWidth * contentHeight;
...
pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight );
...
pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight);

在线演示demo8

查看原文

赞 355 收藏 327 评论 67

幻影 收藏了文章 · 2019-06-11

Javascript 将html转成pdf,下载,支持多页哦(html2canvas 和 jsPDF)

最近碰到个需求,需要把当前页面生成pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

项目源码地址https://github.com/linwalker/...

html2canvas

简介

我们可以直接在浏览器端使用html2canvas,对整个或局部页面进行‘截图’。但这并不是真的截图,而是通过遍历页面DOM结构,收集所有元素信息及相应样式,渲染出canvas image。

由于html2canvas只能将它能处理的生成canvas image,因此渲染出来的结果并不是100%与原来一致。但它不需要服务器参与,整个图片都由客户端浏览器生成,使用很方便。

使用

使用的API也很简洁,下面代码可以将某个元素渲染成canvas:

html2canvas(element, {
    onrendered: function(canvas) {
        // canvas is the final rendered <canvas> element
    }
});

通过onrendered方法,可以将生成的canvas进行回调,比如插入到页面中:

html2canvas(element, {
    onrendered: function(canvas) {
       document.body.appendChild(canvas);
    }
});

做个小例子代码如下,在线展示链接demo1

<html>
  <head>
    <title>html2canvas example</title>
    <style type="text/css">...</style>
  </head>
  <body>
    <header>
      <nav>
        <ul>
          <li>one</li>
          ...
        </ul>
      </nav>
    </header>
    <section>
      <aside>
        <h3>it is a title</h3>
        <a href="">Stone Giant</a>
        ...
     </aside>
      <article>
        <img data-original="./Stone.png">
        <h2>Stone Giant</h2>
        <p>Coming ... </p>
        <p>以一团石头...</p>
      </article>
    </section>
    <footer>write by linwalker @2017</footer>
    <script type="text/javascript" data-original="./html2canvas.js"></script>
    <script type="text/javascript">
        html2canvas(document.body, {
          onrendered:function(canvas) {
            document.body.appendChild(canvas)
          }
        })
    </script>
  </body>
</html>

这个例子将页面body中的元素渲染成canvas,并插入到body中

jsPDF

jsPDF库可以用于浏览器端生成PDF。

文字生成PDF

使用方法如下:

// 默认a4大小,竖直方向,mm单位的PDF
var doc = new jsPDF();

// 添加文本‘Download PDF’
doc.text('Download PDF!', 10, 10);
doc.save('a4.pdf');

在线演示demo2

图片生成PDF

使用方法如下:

// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('landscape','pt',[205, 115])

// 将图片转化为dataUrl
var imageData = ‘data:image/png;base64,iVBORw0KGgo...’;

doc.addImage(imageData, 'PNG', 0, 0, 205, 115);
doc.save('a4.pdf');

在线演示demo3

文字与图片生成PDF

// 三个参数,第一个方向,第二个尺寸,第三个尺寸格式
var doc = new jsPDF('landscape','pt',[205, 155])

// 将图片转化为dataUrl
var imageData = ‘data:image/png;base64,iVBORw0KGgo...’;

//设置字体大小
doc.setFontSize(20);

//10,20这两参数控制文字距离左边,与上边的距离
doc.text('Stone', 10, 20);

// 0, 40, 控制文字距离左边,与上边的距离
doc.addImage(imageData, 'PNG', 0, 40, 205, 115);
doc.save('a4.pdf')

在线演示demo4

生成pdf需要把转化的元素添加到jsPDF实例中,也有添加html的功能,但某些元素无法生成在pdf中,因此可以使用html2canvas + jsPDF的方式将页面转成pdf。通过html2canvas将遍历页面元素,并渲染生成canvas,然后将canvas图片格式添加到jsPDF实例,生成pdf。

html2canvas + jsPDF

单页

将demo1的例子修改下:

<script type="text/javascript" data-original="./js/jsPdf.debug.js"></script>
<script type="text/javascript">
      var downPdf = document.getElementById("renderPdf");
      downPdf.onclick = function() {
          html2canvas(document.body, {
              onrendered:function(canvas) {

                  //返回图片dataURL,参数:图片格式和清晰度(0-1)
                  var pageData = canvas.toDataURL('image/jpeg', 1.0);

                  //方向默认竖直,尺寸ponits,格式a4[595.28,841.89]
                  var pdf = new jsPDF('', 'pt', 'a4');

                  //addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩
                  pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height );

                  pdf.save('stone.pdf');

              }
          })
      }
</script>

在线演示demo5

如果页面内容根据a4比例转化后高度超过a4纸高度呢,生成的pdf会怎么样?会分页吗?

你可以试试,验证一下自己的想法: demo6

jsPDF提供了一个很有用的API,addPage(),我们可以通过pdf.addPage(),来添加一页pdf,然后通过pdf.addImage(...),将图片赋予这页pdf来显示。

那么我们如何确定哪里分页?

这个问题好回答,我们可以设置一个pageHeight,超过这个高度的内容放入下一页pdf。

来捋一下思路,将html页面内容生成canvas图片,通过addImage将第一页图片添加到pdf中,超过一页内容,通过addPage()添加pdf页数,然后再通过addImage将下一页图片添加到pdf中。

嗯~,很好!巴特,难道没有发现问题吗?

这个方法实现的前提是 — — 我们能根据pageHeight先将整页内容生成的canvas图片分割成对应的小图片,然后一个萝卜一个坑,一页一页addImage进去。

What? 想一想我们的canvas是肿么来的,不用拉上去,直接看下面:

html2canvas(document.body, {
    onrendered:function(canvas) {
     //it is here we handle the canvas
    }
})

这里的body就是要生成canvas的元素对象,一个元素生成一个canvas;那么我们需要一页一页的canvas,也就是说。。。

你觉得可能吗? 我觉得不太现实,按这思路要获取页面上不同位置的DOM元素,然后通过htnl2canvas(element,option)来处理,先不说能不能刚好在每个pageHeight的位置刚好找到一个DOM元素,就算找到了,这样做累不累。

累的话
:)可以看看下面这种方法

多页

我提供的思路是我们只生成一个canvas,对就一个,转化元素就是你要转成pdf内容的母元素,在这篇demo里就是body了;其他不变,也是超过一页内容就addPage,然后addImage,只不过这里添加的是同一个canvas。

当然这样做只会出现多页重复的pdf,那到底怎么实现正确分页显示。其实主要利用了jsPDF的两点:

- 超过jsPDF实例格式尺寸的内容不显示
(var pdf = new jsPDF('', 'pt', 'a4'); demo中就是a4纸的尺寸)
- addImage有两个参数可以控制图片在pdf中的位置

虽然每一页pdf上显示的图片是相同的,但我们通过调整图片的位置,产生了分页的错觉。以第二页为例,将竖直方向上的偏移设置为-841.89即一张a4纸的高度,又因为超过a4纸高度范围的图片不显示,所以第二页显示了图片竖直方向上[841.89,1682.78]范围内的内容,这就得到了分页的效果,以此类推。

还是看代码吧:

html2canvas(document.body, {
  onrendered:function(canvas) {

      var contentWidth = canvas.width;
      var contentHeight = canvas.height;

      //一页pdf显示html页面生成的canvas高度;
      var pageHeight = contentWidth / 592.28 * 841.89;
      //未生成pdf的html页面高度
      var leftHeight = contentHeight;
      //页面偏移
      var position = 0;
      //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
      var imgWidth = 595.28;
      var imgHeight = 592.28/contentWidth * contentHeight;

      var pageData = canvas.toDataURL('image/jpeg', 1.0);

      var pdf = new jsPDF('', 'pt', 'a4');

      //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
      //当内容未超过pdf一页显示的范围,无需分页
      if (leftHeight < pageHeight) {
      pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
      } else {
          while(leftHeight > 0) {
              pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
              leftHeight -= pageHeight;
              position -= 841.89;
              //避免添加空白页
              if(leftHeight > 0) {
                pdf.addPage();
              }
          }
      }

      pdf.save('content.pdf');
  }
})

在线演示demo7

两边留边距

修改imgWidth,并且在addImage时x方向参数设置你要的边距,具体代码如下

var imgWidth = 555.28;
var imgHeight = 555.28/contentWidth * contentHeight;
...
pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight );
...
pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight);

在线演示demo8

查看原文

幻影 赞了回答 · 2019-05-24

解决Warning: `defaultValue` is invalid for `getFieldDecorator`

initialValue 的值使用moment()格式化,DatePicker接收的是moment对象

initialValue: moment('2019-01-01')

关注 2 回答 1