最近在开发我司一项目,用的是nuxt.js开发的,nuxt版本号:2.14.8。

主要记录一下,我在这个项目中一些用到的东西吧,地址:http://www.lawlawing.com/

感觉基本包含了nuxt.js的大部分配置了。

安装

第一种办法:

此方法可参考我的这篇文章:初识nuxt.js

我的npm 下载太慢了,所以用了第二种办法

npx create-nuxt-app <project-name>

文档:https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-server

第二种办法:

// 1. 全局安装@vue/cli,已安装的可忽略
> npm i @vue/cli -g
// 2. 拉取nuxt模板
> vue init nuxt/starter {project-name}// 如下显示则为安装完毕
? Project name project-name
? Project description Nuxt.js project
? Author zhanghaoran <zhanghaoran.rem@qq.com>vue-cli · Generated "project-name".To get started:cd project-namenpm install # Or yarnnpm run dev

配置启动端口

同是以本机9363端口启动

第一种办法:

// package.json
{..."config": {"nuxt": {"host": "0.0.0.0","port": "9363"}}
}

第二种办法:

// nuxt.config.js
export default {server: {port: 9363, // default: 3000host: '0.0.0.0', // default: localhost,timing: false}
}

文档:https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-server

配置文件别名(alias)

  1. nuxt.config.js中:
import { resolve } from 'path'module.exports = {alias: {'img': resolve(__dirname, './assets/images'),'css': resolve(__dirname, './assets/css'),},
}
  1. 在组件和页面中使用:
data(){return{img:{logo: require('img/public/logo.png')}}
}<style scoped>@import "css/global.css";
</style>

文档:https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-alias

全局css

// nuxt.config.js
// 这里似乎不能用以上定义的别名路径
css:['~/assets/css/global.css'
],

element-ui按需引入

  1. 下载element-ui与babel
npm i element-ui -S
// 用于按需加载
npm i babel-plugin-component -D
  1. ~/plugins/下创建element-ui.js
// ~/plugins/element-ui.jsimport Vue from 'vue'
import { Input, Select, Button, Option } from 'element-ui'export default ()=>{Vue.use(Input)Vue.use(Select)Vue.use(Button)Vue.use(Option)
}
  1. nuxt.config.js中引入element-ui.js:
// nuxt.config.js
plugins: ['~/plugins/element-ui'
],
  1. nuxt.cofnig.js中配置按需加载
// nuxt.confg.js
build: {vendor: ['element-ui'],babel:{plugins: [["component", {"libraryName": "element-ui", "styleLibraryName": "theme-chalk"}]]},
}

文档:https://element.eleme.cn/#/zh-CN/component/quickstart

nuxt-cookie

  1. 安装

    npm i cookie-universal-nuxt -S
    
  2. nuxt.config.js中引入:

    modules: [   'cookie-universal-nuxt',],
    
  3. 在客户端中使用:

    const path = "/";// 20天const expires = new Date(new Date() * 1 + 86400000 * 20);const tokenName = 'token';this.$cookies.set(tokenName, "token", {path: path, domain: CookieDomain, expires: expires});this.$cookies.remove(tokenName, {path:path, domain: CookieDomain});
    
  4. 在服务端中使用:

    应该是服务端中,比如在下面的axios中使用

文档:https://www.npmjs.com/package/cookie-universal-nuxt

nuxt-axios

  1. 安装

    npm i @nuxtjs/axios -S
    
  2. 配置axios

    // ~/plugins/axios.jsexport default function ({redirect, req, app: { $axios, $cookies }})  {$axios.defaults.baseURL = "http://xxx.com";$axios.onRequest(config => {config.headers.common['Authorization'] = $cookies.get('token');});$axios.interceptors.response.use(response => {// return responseif(response.data.code == 401){// TODO: 清楚本地用户数据, 并重定向到登录页面}else if(response.data.code == 404){// TODO: 接口404}else if(response.data.code == 0){return response.data.data}else{return Promise.reject(response)}},error => {if(error.response.status == 500){// http状态500,服务器内部错误,重定向到500页面redirect("/500.htm")}if(error.response.status == 404){// http状态500,请求API找不到,重定向到404页面redirect("/404.html")}return Promise.reject(error.response)   // 返回接口返回的错误信息})
    }
    
  3. nuxt.config.js中引入

    modules: ['@nuxtjs/axios'
    ],
    plugins: ['~/plugins/axios'
    ],
    
  4. 使用

    // 页面中:// 组件中:

文档:https://www.npmjs.com/package/@nuxtjs/axios

文档:http://www.axios-js.com/zh-cn/docs/nuxtjs-axios.html

环境变量

  1. 下载安装
npm i cross-env -D
  1. 配置启动环境变量
// ~/package.json"scripts": {    "dev": "cross-env MODE=development nuxt",    "build:test": "cross-env MODE=test nuxt build",    "start:test": "cross-env MODE=test nuxt start",    "build": "cross-env MODE=production nuxt build",    "start": "cross-env MODE=production nuxt start",    "generate": "nuxt generate"},
  1. nuxt.config.js中配置env:
export default {    env:{        MODE: process.env.MODE    },}
  1. 创建在各个环境下的接口地址的文件:

    // ~/plugins/env.jsmodule.exports = {    // 开发环境    development: {        MODE: 'development',        API: 'development',        Cookie_Domain: "development"    },    // 测试环境    test:{        MODE: 'test',        API: 'test',        Cookie_Domain: "test"    },    // 线上环境    production: {        MODE: 'production',        API: 'production',        Cookie_Domain: "production"    }}
    
  2. 创建返回当前环境接口地址的文件:

    // ~/plugins/host.jsconst env = require('./env')// 接口export const API = env[process.env.MODE].API;// Cookieexport const CookieDomain = env[process.env.MODE].Cookie_Domain;
    
  3. 使用:

    import {CookieDomain} from "../plugins/host";// 此时,当前系统在开发环境则返回:development;console.log(CookieDomain)this.$cookies.set(tokenName, token, {domain: CookieDomain});
    

一些优化项配置

// nuxt.config.jsmodule.exports = {render: {// 解决nuxt项目首屏加载全部jsresourceHints: false,},build: {analyze: false,        // 可视化打包结果// css文件在测试与生产环境下外部引入extractCSS: process.env.MODE === 'development' ? false : true,// 在开发环境下生成sourcemapproductionSourceMap: process.env.MODE === 'development' ? true : false,// gzip打开productionGzip: true,// gzip的对象productionGzipExtensions: ['js', 'css', 'svg', 'png'],// element-ui按需引入// ... 此处省略,上面有},
}

单行文本与多行文本溢出显示省略号

单行文本

width:300px;
overflow: hidden;
text-overflow:ellipsis;
whitewhite-space: nowrap;

多行文本

display: -webkit-box;    -webkit-box-orient: vertical;    -webkit-line-clamp: 3;    overflow: hidden;

nuxt-link 在新标签页打开

  1. <nuxt-link to="detail" target="_blank">新标签页打开</nuxt-link>
    
  2. 编程式:

    let routeData = this.$router.resolve({    path: "/detail/1",});window.open(routeData.href, '_blank');
    

阻止冒泡事件

event.stopPropagation();

<template>    <div class="contentItem" @click="toDetail()">        <div class="sign"><span>现行有效</span></div>        <div class="download" @click="download($event)"></div>        </div>    </div></template><script>    export default {        name: "contentItem",        methods:{            download(event){                // 阻止冒泡                event.stopPropagation();                console.log('download')            },            toDetail(){                console.log('detail')            }        }    }</script>

输入框默认聚焦

this.$refs.searchInput.focus()

mounted() {    this.$nextTick(() => {     this.$refs.searchInput.focus()    });}

el-input回车提交事件

@keyup.enter.native=“toSearch”

<el-input placeholder="请输入您要检索的关键字" v-model="query" class="input-with-select" ref="searchInput" @keyup.enter.native="toSearch">
</el-input>

自定义表单验证规则

手机号码:

// data()
var isMobile = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/.test(value)
var checkPhone = (rule, value, callback) => {if (!value) {return callback(new Error('请输入手机号码'));}else {if (isMobile.test(value)) {callback();} else {return callback(new Error('请输入正确的手机号'));}}
};// rules
mobile: [{validator: checkPhone, trigger: 'blur'}],

验证码:

// data()
var isVerCode = /^\d{6}$/
var checkCode = (rule, value, callback) =>{if(!value){return callback(new Error('请输入6位验证码'))}else {if (isVerCode.test(value)) {callback();} else {return callback(new Error('请输入正确的验证码'));}}
};
// rules
verificationCode: [{validator: checkCode, trigger: 'blur'}],

密码: 最少6位、两次输入密码一致

// data()var checkPassword = (rule, value, callback) =>{    if(this.register.password != ''){     if(this.register.password.length >= 6){          if(this.register.confirmPassword == this.register.password){                callback();         }else{              return callback(new Error('两次密码不一致'));          }       }else{          return callback(new Error('密码长度6位以上'));     }    }else {        return callback(new Error('密码不能为空'));    }};var checkConfirmPassword = (rule, value, callback) =>{    if(this.register.confirmPassword != ''){       if(this.register.confirmPassword.length >= 6){           if(this.register.confirmPassword == this.register.password){                callback();         }else{              return callback(new Error('两次密码不一致'));          }       }else{          return callback(new Error('密码长度6位以上'));     }    }else {        return callback(new Error('密码不能为空'));    }};// formregister:{    password:'',    confirmPassword: ''},// rulespassword: [{ required: true, message: '请输入注册密码', trigger: 'blur'},{validator: checkPassword, trigger: 'change'}],                    confirmPassword: [{ required: true, message: '请确认注册密码', trigger: 'blur'},{validator: checkConfirmPassword, trigger: 'change'}],

v-if时记得加 :key

// demo
// 我遇到的是:不加key,dom可以切换成功,但是表单验证呀、绑定的数据不能切换;
<template><div v-if="loginType === 'Password'" :key="loginType"></div><div v-if="loginType === 'Code'" :key="loginType"></div><button @click="changeLoginType('Password')">切换为密码登录<button><button @click="changeLoginType('Code')">切换为验证码登录<button>
</template><script>export defalt {data(){return{loginType: Password}}methods:{changeLoginType(loginType){this.loginType = loginType}}}
</script>

部署

我的部署环境是 ubuntu+nginx+node14.x+pm2

  1. 服务器安装环境

    安装Nginx

    sudo apt-get install nginx
    

    安装node

    参考:ubuntu系统下安装/卸载最新版Node、NPM

    安装pm2

    npm i pm2 -g
    
  2. 编写部署配置文件:配置程序以集群方式部署

    在程序根路径下新建文件: ~/ecosystem.config.js

    module.exports = {    apps: [        {            name: 'lawlawing',                // 程序名称,自己修改            exec_mode: 'cluster',            // 模式为集群模式,更多参考pm2文档            instances: 'max', // Or a number of instances            script: './node_modules/nuxt/bin/nuxt.js',            args: 'start',            env: {                "PORT": 9363,             // 启动端口                "HOST": "0.0.0.0"            },            max_memory_restart: "1000M",      // 最大内存重启            autorestart: true                  // 是否自动重启        }    ]}
    
  3. 打包,上传到服务器

    打包

    npm run build
    

    上传服务器

    将打包后的: .nuxt,static,ecosystem.config.js,nuxt.config.js,package.json五个文件/夹 上传到服务器中的程序目录就可以了。

  4. 下载依赖, pm2运行程序

    在刚才上传程序文件的文件下,安装依赖

    npm i
    

    安装完依赖后, 使用pm2启动程序

    pm2 start
    

    两步都是在程序文件的根目录下进行

  5. nginx转发配置

    // 切换到ngxin目录并编辑(我这里直接在nginx.config中添加了,也可以在sites-enabled中添加)cd /etc/nginx vim nginx.config
    

    配置

    map $sent_http_content_type $expires {    "text/html"                 epoch;    "text/html; charset=utf-8"  epoch;    default                     off;}server {    listen 80;    server_name domain.com;    location / {        expires $expires;        proxy_redirect                      off;        proxy_set_header Host               $host;        proxy_set_header X-Real-IP          $remote_addr;        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto  $scheme;        proxy_read_timeout          1m;        proxy_connect_timeout       1m;           // 因为我在第二步配置文件中写的是以9363端口启动程序,所以这里转发9363        proxy_pass                          http://127.0.0.1:9363;    }}
    

    服务器安全组记得开80/443端口

使用Tinymce编辑器

起因

Nuxt.js是一个服务端渲染框架。在一开始项目中使用的是quill.js编辑器,因其支持服务端渲染,但后期其功能过于少,故选择了非常强大的tinymce编辑器。

正文

  • 安装tinymce
npm i @tinymce/tinymce-vue tinymce  -s
  • 在~/static/目录下创建tinymce文件夹,下载完tinymce后在node_modules 中找到 tinymce/skins目录,将其复制到static/tinymce目录。

  • 下载中文语言包。
    下载解压后,把langs文件夹放到~/static/tinymce/下。

  • 在~/components/下创建 tinymceEditor.vue文件。

template

<template><client-only><editor id="tinymce" v-model="contents" :init="init"></editor></client-only>
</template>

script

 import editor from '@tinymce/tinymce-vue';import toolbar from "./toobar";       // toolbarimport plugins from "./plugins";  // pluginsimport {imageHost} from "../../../plugins/host";let tinymce;// 在客户端环境下引入if (process.client) {tinymce = require('tinymce/tinymce');require('tinymce/themes/silver');require('tinymce/icons/default');require('tinymce/plugins/image');// 插入上传图片插件require('tinymce/plugins/media');// 插入视频插件require('tinymce/plugins/table');// 插入表格插件require('tinymce/plugins/lists');// 列表插件require('tinymce/plugins/link');//超链接插件require('tinymce/plugins/wordcount');// 字数统计插件// require('tinymce/plugins/emoticons');// 插入表情插件require('tinymce/plugins/fullscreen');require('tinymce/plugins/code');require('tinymce/plugins/paste');require('tinymce/plugins/advlist');require('tinymce/plugins/autolink');require('tinymce/plugins/autosave');require('tinymce/plugins/codesample');require('tinymce/plugins/colorpicker');require('tinymce/plugins/contextmenu');require('tinymce/plugins/directionality');require('tinymce/plugins/nonbreaking');require('tinymce/plugins/insertdatetime');require('tinymce/plugins/preview');require('tinymce/plugins/print');require('tinymce/plugins/save');require('tinymce/plugins/searchreplace');require('tinymce/plugins/spellchecker');require('tinymce/plugins/tabfocus');require('tinymce/plugins/table');require('tinymce/plugins/template');require('tinymce/plugins/textcolor');require('tinymce/plugins/visualblocks');require('tinymce/plugins/textpattern');require('tinymce/plugins/visualchars');require('tinymce/plugins/wordcount');require('tinymce/plugins/anchor');require('tinymce/plugins/hr');require('tinymce/plugins/link');require('tinymce/plugins/noneditable');require('tinymce/plugins/pagebreak');}export default {name: "tinymce-editor",components:{editor},// 接收引用此组件的值props: ['modifyContent'],data() {return {// 图片地址前缀imageHost:imageHost(),//初始化配置init: {language_url: '/tinymce/langs/zh_CN.js',// 语言包的路径language: 'zh_CN',//语言skin_url: '/tinymce/skins/ui/oxide',// skin路径height: 800,//编辑器高度branding: false,//是否禁用“Powered by TinyMCE”menubar: true,//顶部菜单栏显示object_resizing: false,// 是否禁用表格图片大小调整autosave_ask_before_unload:false,    // 去除关闭/刷新网页时弹出对话框plugins: plugins,toolbar: toolbar,body_class: "panel-body ",end_container_on_empty_block: true,powerpaste_word_import: "clean",code_dialog_height: 450,code_dialog_width: 1000,advlist_bullet_styles: "square",advlist_number_styles: "default",imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"],// 自定义字体集theme_advanced_fonts:"宋体=宋体;微软雅黑=微软雅黑;新宋体=新宋体;Courier New=courier new,courier,monospace;AkrutiKndPadmini=Akpdmi-n",// 自定义字号fontsize_formats: "8pt 10pt 12pt 14pt 18pt 24pt 36pt",content_css:'/tinymce/skins/content/default/content.css',paste_data_images: true, // 允许粘贴图像// 自定义字体font_formats: `微软雅黑=微软雅黑;宋体=宋体;黑体=黑体;仿宋=仿宋;楷体=楷体;隶书=隶书;幼圆=幼圆;思源黑体CN=思源黑体CN;Andale Mono=andale mono,times;Arial=arial, helvetica,sans-serif;Arial Black=arial black, avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats`,content_style: `*                         { padding:0; margin:0; }html, body                { height:100%; }img                       { max-width:100%; display:block;height:auto; }a                         { text-decoration: none; }iframe                    { width: 100%; }p                         { line-height:1.6; margin: 0px; font-size: 16px; font-family:微软雅黑}table                     { word-wrap:break-word; word-break:break-all; max-width:100%; border:none; border-color:#999; }.mce-object-iframe        { width:100%; box-sizing:border-box; margin:0; padding:0; }ul,ol                     { list-style-position:inside; }body, td, pre {font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 12px;
}`,style_formats: [{title: "首行缩进",block: "p",styles: { "text-indent": "2em" }},{title: "行高",items: [{ title: "1", styles: { "line-height": "1" }, inline: "span" },{title: "1.5",styles: { "line-height": "1.5" },inline: "span"},{ title: "2", styles: { "line-height": "2" }, inline: "span" },{title: "2.5",styles: { "line-height": "2.5" },inline: "span"},{ title: "3", styles: { "line-height": "3" }, inline: "span" }]}],default_link_target: "_blank",link_title: false,convert_urls: false, // 图片上传路径为绝对路径remove_script_host: false,paste_word_valid_elements: "*[*]",paste_convert_word_fake_lists: false,paste_webkit_styles: "all",paste_merge_formats: true,nonbreaking_force_tab: false,paste_auto_cleanup_on_paste: false,forced_root_block: "p",force_p_newlines: true,importcss_append: true,tabfocus_elements: ":prev,:next",// 本地图片上传配置images_upload_handler: (blobInfo, success, failure) => {let params = new FormData();params.append("image", blobInfo.blob());this.$axios.post("/upload_image?random="+Math.random(), params).then(res=>{if(res.data.code == 0){success( this.imageHost + res.data.data)} else {failure("上传失败")}}).catch(()=>{failure("上传失败")})},setup: (editor) => {// 监听编辑器失焦事件var _this = this;editor.on('blur', function () {_this.$emit('saveDraft')})},},// 绑定的内容contents: this.modifyContent}},mounted(){ // 用process.client判断一下环境再执行this.$nextTick(()=>{if (process.client) {window.tinymce.init({});}})},watch: {modifyContent(newValue) {this.contents = newValue},contents(newValue) {this.$emit('writeContent', newValue);}}}
  • 引用
<template><div class="postEditor"><tinymceEditor @writeContent="getWriteContent" v-if="isShowEdit"  @saveDraft="saveDraft"/></div>
</template><script>import tinymceEditor from "../../components/public/tinymceEditor/tinymce-editor"export default {name: "write",middleware: 'auth',        // 登录验证中间件components: {tinymceEditor},data() {return {isShowEdit:true,articleContent: '',}},method:{// 同步编辑器数据getWriteContent(content) {this.articleContent = content;},// 保存草稿saveDraft(){console.log("保存草稿")}}

至此,以上配置已经满足我的使用了。
最核心的就是:nuxt.js是服务端渲染。 tinymce编辑器在客户端渲染就好了,和正常vue内引用使用一样

在middleware(中间件)中实现路由鉴权

路由鉴权: 就是判断这个路由当前浏览者是否需要权限访问。
一般我是通过判断cookie中存储的token来判断的。

在middleware文件夹下新建“auth.js”的文件

在当前auth.js文件内判断cookie中是否包含token字段

import getCookie from '~/utils/getCookie'export default function ({route, req, res, redirect}) {let isClient = process.client;let isServer = process.server;let redirectURL = '/sign';var token, path;// 在服务端if (isServer) {// 获取服务端cookielet cookies = getCookie.getcookiesInServer(req)// 获取当前服务端cookie中是否含有token字段token = cookies.token ? cookies.token : ''}// 在客户端if (isClient) {// 获取客户端(本地)cookie中的token字段token = getCookie.getcookiesInClient('token')}// 判断是否获取到token// 未获取到,重定向到登陆页面if (!token) {redirect(redirectURL)}
}

新建获取cookie的文件

~/uitls/getCookie.js

首先:下载js-cookie
npm i js-cookie -s

import Cookie from 'js-cookie'export default {//获取服务端cookiegetcookiesInServer:function (req) {let service_cookie = {};req && req.headers.cookie && req.headers.cookie.split(';').forEach(function (val) {let parts = val.split('=');service_cookie[parts[0].trim()] = (parts[1] || '').trim();});return service_cookie;},//获取客户端cookiegetcookiesInClient:function (key) {return Cookie.get(key) ? Cookie.get(key) : ''}
}

在需要路由鉴权的page页面中使用

比如在 ~/page/index.vue中使用

<script>export default {name: 'index',// auth 为刚才在在middleware文件夹下新建的auth.js文件middleware: 'auth',}
</script>

js-cookie 扩展

1.安装js-cookie

npm install js-cookie --save

2.使用

  1. 引用
    import Cookie from 'js-cookie'

  2. 客户端使用

// 1.获取
Cookie.get(key)
// 2.设置
Cookie.set('name', value, {expires: 过期时间})
// 3.删除
Cookie.remove("name")
// 过期时间设置:
let seconds = 3600;     // 过期时间 /秒
let expires = new Date(new Date() * 1 + seconds * 1000);http://blog.zhanghaoran.ren/detail/16)

加密解密

在~/plugins下新建 encrytion.js文件

const crypto = require('crypto');export default {aesEncrypt(data, key){try{var salt =  key.toString() + 'manbanzhen';    // 定义盐值const cipher = crypto.createCipher('aes192', salt);   // 采用aes192加密方式var crypted = cipher.update(data, 'utf8', 'hex');     // 加密crypted += cipher.final('hex');return crypted;}catch (e) {        // 加捕获是为了在验证失败的时候,直接让用户重新登陆alert("检测到存在安全异常,请检查当前网络环境并重新登录!");window.location.href = '/login'}},aesDecrypt(encrypted, key) {try{var salt =  key.toString() + 'manbanzhen';    // 定义盐值const decipher = crypto.createDecipher('aes192', salt);   // 解密 aes192var decrypted = decipher.update(encrypted, 'hex', 'utf8');    //解密decrypted += decipher.final('utf8');return decrypted;}catch (e) {        // 加捕获是为了在验证失败的时候,直接让用户重新登陆// console.log("error*-**--*-")alert("检测到存在安全异常,请检查当前网络环境并重新登录!");window.location.href = '/login';}}
}

在需要的地方进行引用:

import encrytion from '~/plugins/encrytion'// 加密
encrytion.aesEncrypt('待加密的内容''密钥')
// 解密
encrytion.aesDecrypt('待解密的密文''密钥')

使用vue-cropper实现图片裁剪

最近我司一项目,在个人中心页面上需用户上传个性传封面图(类似知乎个人主页上部),用户上传图片大小不一,且封面图大小为1000px*240px;这就需要在用户上传的时候对图片进行合适大小裁剪。

大概思路:
用户上传图片时

1. 判断图片尺寸大小,宽高不能小于1200px*240*;小于这个尺寸将无法完美裁剪。
2. 将图片转化为base64格式,即时回显图片和不浪费服务器资源。

这两步完成后,将进入到vue-cropper的裁剪操作了,具体操作可见文档,我在下面写了我的配置供参考;
vue-cropper地址: https://github.com/xyxiao001/vue-cropper

裁剪操作结束,点击确认,得到裁剪后返回图片对象为blod类型,但服务器只接收file类型,故需转为file类型给服务器;得到服务器图片路径后,替换掉原本图片路径,完成✅

  1. 下载
npm install vue-cropper
  1. 配置

我使用的是全局配置,此处只写出全局配置

2.1 在~/plugins文件夹下,新建 cropper.js文件;引入vue-cropper

import vueCropper from 'vue-cropper';
import Vue from 'vue';
Vue.use(vueCropper);

2.2 在~/nuxt.config.js文件中,配置插件

export default{plugins: [{ src: '~/plugins/cropper', ssr: false }]
}
  1. 使用
    template
<div><div><input v-if="status" id="uploadFileInput" type="file" accept="image/png,image/jpeg" ></div><div class="cropper-content" v-else><div class="cropper" style="text-align:center"><vueCropperref="cropper":img="option.img":outputSize="option.size":outputType="option.outputType":info="true":full="option.full":canMove="option.canMove":canMoveBox="option.canMoveBox":original="option.original":autoCrop="option.autoCrop":fixed="option.fixed":fixedNumber="option.fixedNumber":centerBox="option.centerBox":infoTrue="option.infoTrue":fixedBox="option.fixedBox":mode="option.mode"></vueCropper></div><div class="btnGroup"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" @click="finish" :loading="loading">确认</el-button></div>
</div>

style

    .cropper-content{width: 100%;height: 300px;}.cropper {width: 100%;height: 240px;}.btnGroup{float: right;margin-top: 10px;margin-right: 15px;}

script

export default{data(){status: true,option: {img: '', // 裁剪图片的地址info: true, // 裁剪框的大小信息outputSize: 0.8, // 裁剪生成图片的质量outputType: 'jpeg', // 裁剪生成图片的格式canScale: true, // 图片是否允许滚轮缩放autoCrop: true, // 是否默认生成截图框autoCropWidth: 1200, // 默认生成截图框宽度autoCropHeight: 240, // 默认生成截图框高度fixedBox: true, // 固定截图框大小 不允许改变fixed: true, // 是否开启截图框宽高固定比例fixedNumber: [25, 6], // 截图框的宽高比例// full: true, // 是否输出原图比例的截图canMoveBox: false, // 截图框能否拖动original: false, // 上传图片按照原始比例渲染centerBox: true, // 截图框是否被限制在图片里面infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高mode: 'cover',    // cover  图片铺满容器},},methods:{// 触发 input:fileuploadFile() {try{this.$refs.upload.querySelector('input').click();}catch (e) {}}, // 点击上传按钮后的事件upload(e) {let file = e.target.files[0];var self = this;this.createReader(file, function (w, h) {if(w<1200 || h<240){self.$message.error("请上传宽度大于 1200px,高度大于 240px 的封面图片。");let UFI = document.getElementById("uploadFileInput");UFI.value = '';}else{self.file2base64(file)}})} ,// 获取图片宽高createReader(file, whenReady) {var reader = new FileReader;reader.onload = function (evt) {var image = new Image();image.onload = function () {var width = this.width;var height = this.height;if (whenReady) whenReady(width, height);};image.src = evt.target.result;};reader.readAsDataURL(file);},// file类型文件 转base64文件类型file2base64(file){var reader = new FileReader();reader.readAsDataURL(file);var self = this;reader.onload = function (e) {// 图片base64化let newUrl = this.result;    //图片路径self.$nextTick(() => {self.pageImage = newUrl;self.option.img = newUrl;self.dialogVisible = true})}},// 点击确定,这一步是可以拿到处理后的地址, 然后上传到服务器finish() {this.$refs.cropper.getCropBlob((data) => {// 将接收到blod文件对象转为filelet file = new File([data], 'proFileCover.jpg',{type: data.type,  lastModified: Date.now()});let params = new FormData();params.append("image", file);var self = this;self.$axios.post("upload_image", params).then(res=>{if(res.data.code == 0){//  显示图片self.pageImage = res.data.data;// 关闭隐藏div,显示图片self.status = false;}  })}},
}

  1. 最后
    通过上述代码操作,就完成了上传图片裁剪的功能。

国际化与本地化(i18n)

更新:2020-11-10

最近我司官网英文版提上了日程,故更新下以前认识浅薄及错误的地方。

欢迎访问我司官网;https://www.qidufanyi.com

有翻译业务也可联系我哟~: 1441576268@qq.com

  1. 安装 vue-i18n
npm install vue-i18n --save
  1. 在 ~/plugins文件夹下新建 i18n.js, 并键入以下代码:
import Vue from 'vue'
import VueI18n from 'vue-i18n'Vue.use(VueI18n)export default ({ app, store }) => {app.i18n = new VueI18n({locale: store.state.locale,fallbackLocale: 'zh-CN',  // 我这里默认语言为中文messages: {'en-US': require('@/locales/en-US.json'),'zh-CN': require('@/locales/zh-CN.json')}})app.i18n.path = (link) => {// 如果是默认语言,就省略if (app.i18n.locale === app.i18n.fallbackLocale) {return `/${link}`}return `/${link}?lang=/${app.i18n.locale}`}
}
  1. 在vuex中保存当前语言状态
    在 ~/store文件夹下 新建 index.js文件,键入以下代码:
export const state = () => ({locales: ['en-US', 'zh-CN'],locale: 'zh-CN'
})export const mutations = {// 此处为设置localeSET_LANG(state, locale) {if (state.locales.indexOf(locale) !== -1) {state.locale = locale}}
}
  1. 在~/middleware文件夹下新建i18n.js文件用来控制语言切换
import utils from "../utils/utils";
export default function ({isHMR, app, store, route, redirect, query,req}) {// 从服务端请求头cookie中获取语种let locale;locale= utils.getcookiesInServer(req).lang// cookie中没有的情况下,获取路由参数中的语种,再没有就默认为中文if(!locale){locale = query.lang || 'zh-CN';}// 设置语种store.commit('SET_LANG', locale); // set storeapp.i18n.locale = store.state.locale;// 跳转该去的地方, isHMR我还没看是什么。if (isHMR) {return;}else if (!query.lang) {return redirect(route.fullPath);}
}
  1. 修改nuxt.config.js文件配置如下:
  router: {middleware: 'i18n'},plugins: ['@/plugins/i18n.js',],
  1. 创建本地语言包
    新建 locales文件夹,创建en-US.json, zh-CN.json两个文件

    要保证语种之间键为一致的

// zh-CN.json
{"about": {"title": "关于",}
}

en-US.json

{"about": {"title": "About",}
}
  1. page文件夹下创建文件
    page/index.vue
<template>
<div><h1>{{$t('about.title')}}</h1><button @click="changeLang('zh-CN')" v-if="$i18n.local!='zh-CN'">中文</button><button @click="changeLang('en-US')" v-if="$i18n.local!='en-US'">英文</button>
</div>
</template><script>
export default {components: {},methods:{changeLang(l) {this.$i18n.locale = l;// 此时语言就会显示为切换的语言},}
};
</script>

欢迎访问旗渡法律翻译来查看最早效果: https://www.qidufanyi.com

另:承接各种翻译业务,包括网站翻译。

原文地址:https://blog.zhanghaoran.ren/article/html/nuxt.jsChangJianWenTiZongJie.html

nuxt.js常见问题总结相关推荐

  1. Vue - 超详细网站接入腾讯地图的完整流程,提供地图显示、IP 属地定位、地理位置名称、获取经纬度等超多功能示例(可一键复制并运行的功能源代码,详细的注释及常见问题汇总)Nuxt.js 也能用!

    前言 如果您需要 uniapp 教程,请访问:uniapp - H5 网页接入腾讯地图. 本文站在小白的角度,实现了 Vue.js / Nuxt.js 网站,集成腾讯地图的详细流程及使用方法教程,提供 ...

  2. nuxt.js实战之移动端rem

    nuxt.js的项目由于是服务端渲染,通过js动态调整不同尺寸设备的根字体这种rem方案存在一种缺陷.由于设置字体的代码不能做服务端渲染,导致服务端返回的代码一开始没有相应的跟字体,直到与前端代码进行 ...

  3. Nuxt.js - nuxt-link与router-link的差异

    前言 在使用Nuxt.js时可能会遇到一个这样的问题? 当打开请求页面的时候,所有页面都被请求了. 这正是<router-link>组件所有的特性. 首先说一下router-link ro ...

  4. Nuxt.js开发中碰到的问题(二)引入gitment评论系统

    博客原文链接 概述 在初步完成功能之后就想着加上评论功能,最开始是想自己实现一个,但是综合考虑之后,还是决定引入 gitment 这个成熟的评论系统代替: 虽然之前在项目中有实现评论系统的相关经验,但 ...

  5. 宝塔面板部署nuxt_MacOS下创建及部署vue.js项目(及Nuxt.js、PM2等相关流程)

    开发环境 权限准备 为避免后续流程出现权限不足的相关提示,首先在终端中输入以下代码检查当前系统是否已开启了对系统文件夹的保护: csrutil status 若结果为csrutil enabled,则 ...

  6. NUXT 入门第一课: 关于 Nuxt.js

    关于 Nuxt.js 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了 Next.js,一个 React 的服务端渲染应用框架.几小时后,与 Next.js 异曲同工,一个基于  ...

  7. 关于 Nuxt.js

    关于 Nuxt.js 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了 Next.js,一个 React 的服务端渲染应用框架.几小时后,与 Next.js 异曲同工,一个基于  ...

  8. 开始使用Nuxt.js

    Nuxt.js简单的说是Vue.js的通用框架,最常用的就是用来作SSR(Server Side Render服务端渲染).再直白点说,就是Vue.js原来是开发SPA(Single Page App ...

  9. Nuxt.js asyncData 多请求

    Nuxt.js框架中asyncData方法只能在pages中的.vue文件页面中使用. 如果要一次发送多个请求可进入如下操作: async asyncData ({ params, error }) ...

最新文章

  1. 转 Android开发学习笔记:浅谈WebView
  2. ASP.NET MVC扩展库
  3. 利用SQL模糊匹配来验证字段是否是日期格式
  4. iOS 导航栏实现总结
  5. LeetCode:226. 翻转二叉树
  6. android fragment面试,Android fragment之间传递数据的方式?
  7. 读书笔记 — Java高并发程序设计 — 第二章 — 基础(上)
  8. atitit.提升开发效率---MDA 软件开发方式的革命(3)----自动化建表
  9. android陀螺仪方向,获得陀螺仪传感器Android的输出
  10. 知名人寿保险品牌SCRM成功案例-全员营销方案赋能 提升客户管理能力
  11. hadoop-2.6下载地址
  12. c++中计算2得n次方_南大专转本 2020年计算机考前模拟(A)
  13. Laravel本地Sail开发环境下Phpstorm+浏览器+Postman调试配置
  14. 微信支付之01------获取订单微信支付二维码的接口------Java实现
  15. 清北学堂2019.7.17
  16. 从柏拉图采花问题说起
  17. 2022-2028全球激光清洗设备行业调研及趋势分析报告
  18. shutil模块:复制文件和文件夹,文件压缩
  19. 爬取新浪微博新闻,包括模拟登陆,数据存储等!最适合新手的教程
  20. c#中的vector_.NET Core 3 中的性能提升

热门文章

  1. 华为畅享10plus搭在s鸿蒙系统,华为畅享10 Plus 好看又好用,这是真的香
  2. 华为麒麟990国产(笔记本/UOS操作系统)搭载h5stream视频流媒体软件
  3. docker实战2 (docker swarm的应用,docker集群的构建,在docker集群中部署服务的创建与更新)
  4. [Jmeter常见使用方法]
  5. 【译】来,用 SVG CSS 给你画一朵真实的云
  6. MySQL 获取今日、昨日、本周、上周、本月、上月、本年、去年日期
  7. PTA L1-039:古风排版 (python)
  8. 《迷失岛2》游戏开发框架开发日记:背包基本逻辑
  9. 企业直播营销有什么优势
  10. 大数讯智能电销机器人,让电销从此变得轻松!