Qt Quick综合实例之文件查看器
假设你基于Qt SDK 5.3.1来创建一个Qt Quick App项目,项目模板为你准备的main.qml文档的根元素是ApplicationWindow或Window。这次我们就以ApplicationWindow为例,环绕着它实现一个综合实例:文件查看器。
通过文件查看器的实现。我们来再次领略一下Qt Quick的犀利。
版权全部foruok,转载请注明出处:http://blog.csdn.net/foruok。
本实例将会用到下列特性:
- ApplicationWindow
- MenuBar
- ToolBar、ToolButton
- Action
- StatusBar
- MediaPlayer
- Image
- XMLHttpRequest
- ColorDialog
- FileDialog
- TextArea
- 动态创建QML组件
- 多界面切换
我们之前的文章都是就某一个主题来展开的,这次要出个大招。
先看点儿图。
文件查看器效果
图1,初始状态
图1 初始状态
图2 是浏览文本文件的效果:
图2 浏览文本文件
图3是浏览图片:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZm9ydW9r/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
图3 浏览图片
图4是播放视频:
图4 播放视频
图5是播放音乐:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZm9ydW9r/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
图5 播放音乐
好啦,看代码前,先大概介绍下用到的Qt Quick元素。
Image、动态创建组件、界面切换这些没什么好说的。我之前的文章中已经涉及到。仅仅讲新的了。
ApplicationWindow
ApplicationWindow。相似Qt C++中的QMainWindow。请对比着理解。
ApplicationWindow有菜单条(menuBar属性)、工具栏(toolBar属性)、状态栏(statusBar属性)。
咦,没有centralWidget哈,别急,有的,就是ContentItem,就是那个你不与不论什么属性绑定的Item。比如:
ApplicationWindow{
...
Rectangle{
id: centralView;
anchors.fill: parent;
color: "red";
}
}
上面代码中的centralView就相应QMainWindow的centralWidget了。
看起来没有DockWidgets……兄弟,别要求太高了。你想要么,真的想要么。你真的确定你想要么?好吧,自己实现就好了,QML里非常多元素的哈。
MenuBar
MenuBar就是菜单条一长条区域了。它干两件事:
- 维护一个Menu列表(menus属性)
- 绘制菜单条背景色(style属性)
Menu
看截图中的“文件”、“设置”、“帮助”三个菜单,点一下弹出一个子菜单,Menu就代表文件这个菜单和它以下的子菜单。
列表属性items指向Menu的子菜单项,它的类型是list<Object>,是默认属性。
title属性是string类型。你看到的“文件”、“设置”等字样就是它指定的。
有这两个属性,Menu就能够開始干活了。看Qt帮助里的演示样例代码片段:
Menu {
title: "Edit" MenuItem {
text: "Cut"
shortcut: "Ctrl+X"
onTriggered: ...
} MenuItem {
text: "Copy"
shortcut: "Ctrl+C"
onTriggered: ...
} MenuItem {
text: "Paste"
shortcut: "Ctrl+V"
onTriggered: ...
} MenuSeparator { } Menu {
title: "More Stuff" MenuItem {
text: "Do Nothing"
}
}
}
MenuItem
Menu的孩子啊,最受待见的就是MenuItem了。
MenuItem代表一个具体的菜单项,点一下就干一件事情的角色。
你能够实现onTriggered信号处理响应用户对它的选择。
MenuItem的text属性指定菜单文字,iconSource属性指定菜单图标。
Action属性非常强大哦。MenuItem的text、iconSource、trigger信号等实现的效果,都能够通过Action来实现。这两种方式是等同的。
咱们来看Action。
Action
Action类有text、iconSource等属性,还有toggled、triggered两个信号。这些在MenuItem里有相应的属性,不必多说了。
使用Action的一大优点是。你能够给它指定一个id(比方叫open),然后在使用ToolButton构造ToolBar时指定ToolBatton的action属性为之前定义的id为open的那个action,这样工具栏的button就和菜单关联起来了。
好啦,总结一下,实现菜单条的典型代码结构是酱紫的:
MenuBar {
Menu{
title:"文件";
MenuItem{
text:"打开";
iconSource: "res/ic_open.png";
onTriggered:{
...
} }
MenuItem{
action: Action {
text:"保存";
iconSource: "res/ic_save.png";
onTriggered:{
...
}
}
}
}
}
如你所见,我使用了两种构造MenuItem的方式,一种用到了Action。一种没有。
ToolBar
ToolBar就是工具栏相应的类,它仅仅有一个属性。contentItem。类型为Item。
一般我们能够将一个Row或者RowLayout对象赋值给contentItem,而Row或RowLayout则管理一组ToolButton来作为工具栏上的button。
ToolButton
ToolButton是Button的派生类。专为ToolBar而生,普通情况下定义ToolButton对象时仅仅须要指定其iconSource属性就可以。比如:
ToolButton {
iconSource: "res/ic_open.png";
}
另一种方式是将一个已定义好的Action对象关联到ToolButton对象上。
比如:
ToolButton{
action: openAction;
}
这种话。ToolButton会使用Action定义的iconSource或iconName作为其图标。
好啦。构造工具栏的典型代码结构例如以下:
ToolBar{
RowLayout {
ToolButton{
action: textAction;
}
ToolButton{
action: imageAction;
}
ToolButton{
iconSource: "res/exit.png";
onClicked:{
...
}
}
}
}
状态栏
ApplicationWindow的属性statusBar代表状态栏,其类型为Item。你能够将随意的Item赋值给它,能够随心所欲构建你妖娆多姿的状态栏。比方这样:
ApplicationWindow{
statusBar: Text {
text: "status bar";
color: "blue";
}
}
MediaPlayer
Qt Quick里,播放视频、音频文件都直接使用MediaPlayer类,它是万能的,不要说你万万没想到哈。
对于音乐,最简单。设置source属性。调用play()方法,就能够了。比如:
ApplicationWindow{
...
MediaPlayer{
id: player;
source: "xxx.mp3";
}
Component.onCompleted:{
player.play();
}
}
对于视频,MediaPlayer还得寻求场外帮助,求助对象就是它的好基友:VideoOutput 。简单的演示样例代码:
ApplicationWindow{
...
MediaPlayer {
id: player;
source: "test.ts";
} VideoOutput {
anchors.fill: parent;
source: player;
} Component.onCompleted: player.play();
}
哦,黄小琥唱过的歌:没那么简单。是的,实际做项目比較复杂。你还要关注各种播放状态,要seek,要pause。要处理错误,简直烦死人了。
请移步到Qt帮助里了解详情。
XMLHttpRequest
在Qt Quick里,要訪问网络肿么办泥?答案是:XMLHttpRequest !
不要被它傲娇的外表迷惑。以为它仅仅接受XML文档,事实上,它什么都能处理,txt、html、json、binary……它温和坚定强悍无比。
本实例用它来载入本地的文本文件,耶。这样都能够哈。
谁叫Qt Quick不提供直接訪问本地文件的类库呢!
我可不想跑到C++里用QFile、QTextStream这对黄金搭档。
XMLHttpRequest的文档,Qt帮助里语焉不详,仅仅有一个演示样例。请看这里:
http://www.w3school.com.cn/xml/xml_http.asp
TextArea
这有什么好讲的,看Qt帮助吧。仅仅提一点:
TextArea自己主动处理翻页按键、上下键、鼠标中键,正确的滚动文本。而TextEdit,抱歉。我是来打酱油的。
标准对话框
Qt Quick提供了非常多标准对话框,比方FileDialog用来选择文件或目录。ColorDialog用来选择颜色,MessageDialog用来显示一些提示信息。这些我们实例中用到了。參考Qt帮助吧。
我仅仅说点儿经验。
我一開始使用qmlscene来载入main.qml。出来的界面比較正常,工具栏的图标、菜单项前也有图标。但是当我创建了一个Qt Quick App。灵异事件发生了:
菜单项前面没有图标了……
工具栏图标好大好大……
颜色对话框、消息框,点击右上角的关闭button。收不到rejected信号啊……
查了老半天,猛回头,警世钟响起,我了悟了。
原来是酱紫的:
Qt Quick提供了这些标准对话框的默认实现,假设应用执行的平台没有可用的。就用这些默认实现。
那在Windows上,假设你的main()函数,使用QGuiApplication而非QApplication,就会用到Qt Quick实现的版本号,一切都变了模样
资源管理
Qt SDK 5.3之后,Qt Creator创建的Qt Quick App项目,就为我们建立了一个qrc文件,把main.qml扔里啦。我在实例中也把非常多图标扔里了。
使用qrc来管理资源,这是跨平台的,推荐使用。
我还自己画了些图标,真费劲。弄得也不好看。只是应用的大眼睛图标,看起来还像那么一回事儿。
源代码
前戏太长,或许你已经失去了兴趣。好吧,G点来咧。
QML代码
版权全部foruok。转载请注明出处:http://blog.csdn.net/foruok。
全部QML代码都在这里了,居然有450行啊亲。
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import QtMultimedia 5.0 ApplicationWindow {
visible: true
width: 480
height: 360;
color: "black";
title: "文件查看器";
id: root;
property var aboutDlg: null;
property var colorDlg: null;
property color textColor: "green";
property color textBackgroundColor: "black"; menuBar: MenuBar{
Menu {
title: "文件";
MenuItem{
iconSource: "res/txtFile.png";
action: Action{
id: textAction;
iconSource: "res/txtFile.png";
text: "文本文件";
onTriggered: {
fileDialog.selectedNameFilter = fileDialog.nameFilters[0];
fileDialog.open();
}
tooltip: "打开txt等文本文件";
}
}
MenuItem{
action: Action {
id: imageAction;
text: "图片";
iconSource: "res/imageFile.png";
onTriggered: {
fileDialog.selectedNameFilter = fileDialog.nameFilters[1];
fileDialog.open();
}
tooltip: "打开jpg等格式的图片";
}
}
MenuItem{
action: Action {
id: videoAction;
iconSource: "res/videoFile.png";
text: "视频";
onTriggered: {
fileDialog.selectedNameFilter = fileDialog.nameFilters[2];
fileDialog.open();
}
tooltip: "打开TS、MKV、MP4等格式的文件";
}
}
MenuItem{
action: Action {
id: audioAction;
iconSource: "res/audioFile.png";
text: "音乐";
onTriggered: {
fileDialog.selectedNameFilter = fileDialog.nameFilters[3];
fileDialog.open();
}
tooltip: "打开mp3、wma等格式的文件";
}
}
MenuItem{
text: "退出";
onTriggered: Qt.quit();
}
}
Menu {
title: "设置";
MenuItem {
action: Action {
id: textColorAction;
iconSource: "res/ic_textcolor.png";
text: "文字颜色";
onTriggered: root.selectColor(root.onTextColorSelected);
}
}
MenuItem {
action: Action{
id: backgroundColorAction;
iconSource: "res/ic_bkgndcolor.png";
text: "文字背景色";
onTriggered: root.selectColor(root.onTextBackgroundColorSelected);
}
}
MenuItem {
action: Action{
id: fontSizeAddAction;
iconSource: "res/ic_fontsize2.png";
text: "增大字体";
onTriggered: textView.font.pointSize += 1;
}
}
MenuItem {
action: Action{
id: fontSizeMinusAction;
iconSource: "res/ic_fontsize1.png";
text: "减小字体";
onTriggered: textView.font.pointSize -= 1;
}
}
}
Menu {
title: "帮助";
MenuItem{
text: "关于";
onTriggered: root.showAbout();
}
MenuItem{
text: "訪问作者博客";
onTriggered: Qt.openUrlExternally("http://blog.csdn.net/foruok");
}
}
} toolBar: ToolBar{
RowLayout {
ToolButton{
action: textAction;
}
ToolButton{
action: imageAction;
}
ToolButton{
action: videoAction;
}
ToolButton{
action: audioAction;
}
ToolButton{
action: textColorAction;
}
ToolButton {
action: backgroundColorAction;
}
ToolButton {
action: fontSizeAddAction;
}
ToolButton {
action: fontSizeMinusAction;
}
}
} statusBar: Rectangle {
color: "lightgray";
implicitHeight: 30;
width: parent.width;
property alias text: status.text;
Text {
id: status;
anchors.fill: parent;
anchors.margins: 4;
font.pointSize: 12;
}
} Item {
id: centralView;
anchors.fill: parent;
visible: true;
property var current: null;
BusyIndicator {
id: busy;
anchors.centerIn: parent;
running: false;
z: 3;
}
Image {
id: imageViewer;
anchors.fill: parent;
visible: false;
asynchronous: true;
fillMode: Image.PreserveAspectFit;
onStatusChanged: {
if (status === Image.Loading) {
centralView.busy.running = true;
}
else if(status === Image.Ready){
centralView.busy.running = false;
}
else if(status === Image.Error){
centralView.busy.running = false;
centralView.statusBar.text = "图片无法显示";
}
}
} TextArea {
id: textView;
anchors.fill: parent;
readOnly: true;
visible: false;
wrapMode: TextEdit.WordWrap;
font.pointSize: 12;
style: TextAreaStyle{
backgroundColor: root.textBackgroundColor;
textColor: root.textColor;
selectionColor: "steelblue";
selectedTextColor: "#a00000";
} property var xmlhttp: null;
function onReadyStateChanged(){
if(xmlhttp.readyState == 4){
text = xmlhttp.responseText;
xmlhttp.abort();
}
} function loadText(fileUrl){
if(xmlhttp == null){
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = onReadyStateChanged;
}
if(xmlhttp.readyState == 0){
xmlhttp.open("GET", fileUrl);
xmlhttp.send(null);
}
}
} VideoOutput {
id: videoOutput;
anchors.fill: parent;
visible: false;
source: player;
onVisibleChanged: {
playerState.visible = visible;
}
MouseArea {
anchors.fill: parent;
onClicked: {
switch(player.playbackState){
case MediaPlayer.PausedState:
case MediaPlayer.StoppedState:
player.play();
break;
case MediaPlayer.PlayingState:
player.pause();
break;
}
}
}
} Rectangle {
id: playerState;
color: "gray";
radius: 16;
opacity: 0.8;
visible: false;
z: 2;
implicitHeight: 80;
implicitWidth: 200;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 16;
Column {
anchors.fill: parent;
anchors.leftMargin: 12;
anchors.rightMargin: 12;
anchors.topMargin: 6;
anchors.bottomMargin: 6;
spacing: 4;
Text {
id: state;
font.pointSize: 14;
color: "blue";
}
Text {
id: progress;
font.pointSize: 12;
color: "white";
}
}
} MediaPlayer {
id: player; property var utilDate: new Date();
function msecs2String(msecs){
utilDate.setTime(msecs);
return Qt.formatTime(utilDate, "mm:ss");
}
property var sDuration; onPositionChanged: {
progress.text = msecs2String(position) + sDuration;
}
onDurationChanged: {
sDuration = " / " + msecs2String(duration);
}
onPlaybackStateChanged: {
switch(playbackState){
case MediaPlayer.PlayingState:
state.text = "播放中";
break;
case MediaPlayer.PausedState:
state.text = "已暂停";
break;
case MediaPlayer.StoppedState:
state.text = "停止";
break;
}
}
onStatusChanged: {
switch(status){
case MediaPlayer.Loading:
case MediaPlayer.Buffering:
busy.running = true;
break;
case MediaPlayer.InvalidMedia:
root.statusBar.text = "无法播放";
case MediaPlayer.Buffered:
case MediaPlayer.Loaded:
busy.running = false;
break;
}
}
}
} function processFile(fileUrl, ext){
var i = 0;
for(; i < fileDialog.nameFilters.length; i++){
if(fileDialog.nameFilters[i].search(ext) != -1) break;
}
switch(i){
case 0:
//text file
if(centralView.current != textView){
if(centralView.current != null){
centralView.current.visible = false;
}
textView.visible = true;
centralView.current = textView;
}
textView.loadText(fileUrl);
break;
case 1:
if(centralView.current != imageViewer){
if(centralView.current != null){
centralView.current.visible = false;
}
imageViewer.visible = true;
centralView.current = imageViewer;
}
imageViewer.source = fileUrl;
break;
case 2:
case 3:
if(centralView.current != videoOutput){
if(centralView.current != null){
centralView.current.visible = false;
}
videoOutput.visible = true;
centralView.current = videoOutput;
}
player.source = fileUrl;
player.play();
break;
default:
statusBar.text = "抱歉,处理不了";
break;
}
} function showAbout(){
if(aboutDlg == null){
aboutDlg = Qt.createQmlObject(
'import QtQuick 2.2;import QtQuick.Dialogs 1.1;MessageDialog{icon: StandardIcon.Information;title: "关于";\ntext: "仅仅是个演示样例撒";\nstandardButtons:StandardButton.Ok;}'
, root, "aboutDlg");
aboutDlg.accepted.connect(onAboutDlgClosed);
aboutDlg.rejected.connect(onAboutDlgClosed);
aboutDlg.visible = true;
} } function selectColor(func){
if(colorDlg == null){
colorDlg = Qt.createQmlObject(
'import QtQuick 2.2;import QtQuick.Dialogs 1.1;ColorDialog{}',
root, "colorDlg");
colorDlg.accepted.connect(func);
colorDlg.accepted.connect(onColorDlgClosed);
colorDlg.rejected.connect(onColorDlgClosed);
colorDlg.visible = true;
}
} function onAboutDlgClosed(){
aboutDlg.destroy();
aboutDlg = null;
} function onColorDlgClosed(){
colorDlg.destroy();
colorDlg = null;
} function onTextColorSelected(){
root.textColor = colorDlg.color;
} function onTextBackgroundColorSelected(){
root.textBackgroundColor = colorDlg.color;
} FileDialog {
id: fileDialog;
title: qsTr("Please choose an image file");
nameFilters: [
"Text Files (*.txt *.ini *.log *.c *.h *.java *.cpp *.html *.xml)",
"Image Files (*.jpg *.png *.gif *.bmp *.ico)",
"Video Files (*.ts *.mp4 *.avi *.flv *.mkv *.3gp)",
"Audio Files (*.mp3 *.ogg *.wav *.wma *.ape *.ra)",
"*.*"
];
onAccepted: {
var filepath = new String(fileUrl);
//remove file:///
if(Qt.platform.os == "windows"){
root.statusBar.text = filepath.slice(8);
}else{
root.statusBar.text = filepath.slice(7);
}
var dot = filepath.lastIndexOf(".");
var sep = filepath.lastIndexOf("/");
if(dot > sep){
var ext = filepath.substring(dot);
root.processFile(fileUrl, ext.toLowerCase());
}else{
root.statusBar.text = "Not Supported!";
}
}
}
}
C++代码
事实上,我仅仅对模板生成的C++代码修改了三行,一行include,一行QApplication,一行设置应用图标。main.cpp例如以下:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QIcon> int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setWindowIcon(QIcon(":/res/eye.png")); QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); return app.exec();
}
PRO文件
有人喊。兄弟。这也要贴!你凑字数呢……你管,就放这里了:
TEMPLATE = app QT += qml quick network multimedia widgets SOURCES += main.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = # Default rules for deployment.
include(deployment.pri) HEADERS +=
版权全部foruok。转载请注明出处:http://blog.csdn.net/foruok。
好啦。彪悍的人生不须要解释,都扒光了,你自己细致看吧。
回想一下我的Qt Quick系列文章:
Qt Quick综合实例之文件查看器的更多相关文章
- 一步一步学Silverlight 2系列(18):综合实例之RSS阅读器
一步一步学Silverlight 2系列(18):综合实例之RSS阅读器 概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支 ...
- 02-IOSCore - NSFileHandle、合并文件、文件指针、文件查看器
[day0201_NSFileHandle]:文件句柄 1 NSFileHandle 文件对接器.文件句柄 常用API: - (NSData *)readDataToEndOfFile;读取数据到最后 ...
- WPF 模仿 UltraEdit 文件查看器系列一 用户控件
WPF 模仿 UltraEdit 文件查看器系列一 用户控件 运行环境:Win10 x64, NetFrameWork 4.8, 作者:乌龙哈里,日期:2019-05-10 章节: 起步 添加用户控件 ...
- WPF 模仿 UltraEdit 文件查看器系列 开篇和导读
WPF 模仿 UltraEdit 文件查看器系列 开篇和导读 运行环境:Win10 x64, NetFrameWork 4.8, 作者:乌龙哈里,日期:2019-05-10 学 .Net FrameW ...
- Qt Quick 图像处理实例之美图秀秀(附源代码下载)
在<Qt Quick 之 QML 与 C++ 混合编程具体解释>一文中我们解说了 QML 与 C++ 混合编程的方方面面的内容,这次我们通过一个图像处理应用.再来看一下 QML 与 C++ ...
- 安装XPS文件查看器的方法
方法1https://jingyan.baidu.com/article/ca2d939d6eb0eeeb6c31cecb.html 方法2 Win10需要使用这种方式,因为Windows 10:版本 ...
- PHP 简易文件查看器
超简易服务器端文件查询器 代码如下: <?php // 系统入口 date_default_timezone_set("PRC"); error_reporting(E_AL ...
- Windows蓝屏dump文件查看器(转)
Windbg-分析Windows蓝屏原因利器[转]下载地址先声明下,虽然用windbg诊断蓝屏之前网络上已经有人发过教程了,但就我而言, 学会使用windbg来诊断蓝屏也算是自己的原创吧.以前看一个微 ...
- Eclipse下jad反编译之“类文件查看器”不能处理给定的输入错误解决
Eclipse中的插件下载,安装和配置可以参考我的另一篇文章:MyEclipse反编译Class文件 下面重点讲解如何使用jad反编译 1.在DOS窗口中,到class所在目录,直接运行 >ja ...
随机推荐
- docloud后台管理项目(开篇)
最近朋友做app需要web做后台管理,所以花了一周时间做了这个项目. 废话不多说,开发环境是nginx+php5.3,使用thinkphp框架.是一个医疗器械数据统计的后台,业务功能很简单就是查看用户 ...
- PHPStorm+XDebug进行调试
笔者的开发环境如下: Windows8.1+Apache+PhpStorm+XDebug+Firefox(XDebug helper 1.4.3插件). 一.XDebug安装配置 (1)下载XDebu ...
- UI布局只关乎三件事情:尺寸、位置、组织
UI布局只关乎三件事情:尺寸.位置.组织. 组织分为两类: 单元组织: 集合组织: 混合组织.
- MFC窗体大小变化
对话框的大小变化后,假若对话框上的控件大小不变化,看起来会比较难看.下面就介绍怎么让对话框上的控件随着对话框的大小的变化自动调整. 首先明确的是Windows有一个WM_SIZE消息响应函数,这个函数 ...
- CAD与用户交互在图面上选择一个实体(com接口VB语言)
主要用到函数说明: IMxDrawUtility::GetEntity 与用户交互到在图面上选择一个实体,详细说明如下: 参数 说明 [out] IMxDrawPoint** pPickPoint 返 ...
- java_IO_3
Reader和Writer针对字符文件 对图片类文件可能就显得无能为力了 会损坏文件 package ioStudy; import java.io.File; import java.io.Fi ...
- 01Hypertext Preprocessor
Hypertext Preprocessor PHP即Hypertext Preprocessor是一种被广泛使用的开放源代码多用途动态交互性站点的强有力的服务器端脚本语言尤其适用于 Web开发人员可 ...
- Spring Boot 与消息
一.消息概述 在大多数应用中,可以通过消息服务中间件来提升系统的异步通信.扩展解耦和流量削峰等能力. 当消息发送者发送消息后,将由消息代理接管,消息代理保证消息传递到指定目的地. 消息队列主要有两种形 ...
- vuecli开发项目,文件打包后,appjs/vendorjs文件过大
项目上线后,浏览器第一次加载会特别特别慢,network中看到vendorjs文件1.9M,不慢才怪. echarts按需引入后,也有1.1M左右,由于对vue脚手架理解不深,自己扒了大量的文档,又测 ...
- Word 格式优化
Word 格式优化. Word 支持 VBA 意味着,可以编程实现自己想要的格式拓展. Word 代码布局