<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cordova 教程 &#8211; FoxDevelop</title>
	<atom:link href="https://www.foxdevelop.com/zh/category/cordova%e6%95%99%e7%a8%8b/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.foxdevelop.com</link>
	<description>独立软件开发工作室</description>
	<lastBuildDate>Wed, 29 Jul 2026 03:44:35 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>

<image>
	<url>https://www.foxdevelop.com/wp-content/uploads/2026/05/fox-svgrepo-com-1.png</url>
	<title>Cordova 教程 &#8211; FoxDevelop</title>
	<link>https://www.foxdevelop.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>播放音频</title>
		<link>https://www.foxdevelop.com/zh/2026/07/29/%e6%92%ad%e6%94%be%e9%9f%b3%e9%a2%91/</link>
					<comments>https://www.foxdevelop.com/zh/2026/07/29/%e6%92%ad%e6%94%be%e9%9f%b3%e9%a2%91/#respond</comments>
		
		<dc:creator><![CDATA[jack]]></dc:creator>
		<pubDate>Wed, 29 Jul 2026 03:44:17 +0000</pubDate>
				<category><![CDATA[Cordova 教程]]></category>
		<guid isPermaLink="false">https://www.foxdevelop.com/?p=6720</guid>

					<description><![CDATA[我们需要点击卡片，播放音频。 请先把音频mp3文件，放到ww ...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">我们需要点击卡片，播放音频。</p>



<p class="wp-block-paragraph">请先把音频mp3文件，放到www/audio目录下面</p>



<p class="wp-block-paragraph">我们是演示项目，所以这里把一个b.mp3放到目录中就可以。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="372" height="39" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-335.png" alt="" class="wp-image-6721" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-335.png 372w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-335-300x31.png 300w" sizes="(max-width: 372px) 100vw, 372px" /></figure>



<h4 class="wp-block-heading">方式1-HTML5播放</h4>



<p class="wp-block-paragraph">通过WEBVIEW的支持的HTML5 Audio来播放音频，无序插件</p>



<p class="wp-block-paragraph">修改main.js 内容如下</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-comment">// Dữ liệu phụ âm tiếng Việt</span>
<span class="hljs-keyword">const</span> consonants = &#91;
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"b"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɓ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bật môi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"c"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/k/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm vòm ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ch"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/tɕ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát vòm"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"d"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɗ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bật răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"đ"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɗ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm đệm răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"g"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɣ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát vòm ngạc"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"gh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɣ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Dùng trước e, i"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"h"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/h/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm thanh hơi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"k"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/k/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"kh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/x/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"l"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/l/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bên răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"m"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/m/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi môi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"n"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/n/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ng"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ŋ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi ngạc mềm"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ngh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ŋ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Dùng trước e,i"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"nh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɲ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi vòm ngạc"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"p"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/p/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc môi không bật"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ph"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/f/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát môi răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"q"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/kw/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Luôn đi với u"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"r"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/r/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm rung răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"s"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/s/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"t"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/t/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"th"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/θ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"v"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/v/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát môi răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"x"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/s/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng lợi"</span>}
];

<span class="hljs-keyword">let</span> favList = <span class="hljs-built_in">JSON</span>.parse(localStorage.getItem(<span class="hljs-string">"vn_consonant_fav"</span>)) || &#91;];
<span class="hljs-keyword">let</span> currentAudio = <span class="hljs-literal">null</span>; <span class="hljs-comment">// 全局音频对象，用于停止上一首</span>

<span class="hljs-keyword">const</span> gridEl = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"consonant-grid"</span>);
<span class="hljs-keyword">const</span> favGridEl = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"favorite-grid"</span>);
<span class="hljs-keyword">const</span> modal = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal"</span>);
<span class="hljs-keyword">const</span> modalChar = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-char"</span>);
<span class="hljs-keyword">const</span> modalPro = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-pronounce"</span>);
<span class="hljs-keyword">const</span> modalDesc = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-desc"</span>);
<span class="hljs-keyword">const</span> btnFav = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"btn-fav"</span>);
<span class="hljs-keyword">const</span> closeModal = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"close-modal"</span>);

<span class="hljs-keyword">let</span> currentItem = <span class="hljs-literal">null</span>;

<span class="hljs-comment">// ====================== HÀM PHÁT MP3 ======================</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playAudio</span>(<span class="hljs-params">charName</span>) </span>{
    <span class="hljs-comment">// Dừng âm thanh đang phát</span>
    <span class="hljs-keyword">if</span>(currentAudio){
        currentAudio.pause();
        currentAudio.currentTime = <span class="hljs-number">0</span>;
    }
    <span class="hljs-comment">// Tạo audio</span>
    currentAudio = <span class="hljs-keyword">new</span> Audio(<span class="hljs-string">`audio/<span class="hljs-subst">${charName}</span>.mp3`</span>);
    currentAudio.volume = <span class="hljs-number">1</span>;
    currentAudio.play().catch(<span class="hljs-function"><span class="hljs-params">err</span>=&gt;</span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Không phát được file: "</span>, charName, err);
        alert(<span class="hljs-string">`Không tìm thấy file âm thanh: <span class="hljs-subst">${charName}</span>.mp3`</span>);
    });
}

<span class="hljs-comment">// Render thẻ phụ âm</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderCards</span>(<span class="hljs-params"></span>)</span>{
    gridEl.innerHTML = <span class="hljs-string">""</span>;
    consonants.forEach(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.className = <span class="hljs-string">"bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer"</span>;
        div.innerHTML = <span class="hljs-string">`&lt;span class="text-2xl font-bold text-gray-800"&gt;<span class="hljs-subst">${item.char}</span>&lt;/span&gt;`</span>;
        div.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span> {
            playAudio(item.char); <span class="hljs-comment">// Nhấn thẻ phát mp3</span>
            openModal(item);
        };
        gridEl.appendChild(div);
    })
}

<span class="hljs-comment">// Render trang yêu thích</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderFav</span>(<span class="hljs-params"></span>)</span>{
    favGridEl.innerHTML = <span class="hljs-string">""</span>;
    <span class="hljs-keyword">if</span>(favList.length === <span class="hljs-number">0</span>){
        favGridEl.innerHTML = <span class="hljs-string">`&lt;p class="col-span-3 text-center text-gray-400 py-10"&gt;Chưa có từ yêu thích&lt;/p&gt;`</span>;
        <span class="hljs-keyword">return</span>;
    }
    favList.forEach(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.className = <span class="hljs-string">"bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer"</span>;
        div.innerHTML = <span class="hljs-string">`&lt;span class="text-2xl font-bold text-gray-800"&gt;<span class="hljs-subst">${item.char}</span>&lt;/span&gt;`</span>;
        div.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span> {
            playAudio(item.char);
            openModal(item);
        };
        favGridEl.appendChild(div);
    })
}

<span class="hljs-comment">// Mở popup chi tiết</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">openModal</span>(<span class="hljs-params">item</span>)</span>{
    currentItem = item;
    modalChar.innerText = item.char;
    modalPro.innerText = item.pronounce;
    modalDesc.innerText = item.desc;
    updateFavButton();
    modal.classList.remove(<span class="hljs-string">"hidden"</span>);
}

<span class="hljs-comment">// Đóng popup</span>
closeModal.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
    modal.classList.add(<span class="hljs-string">"hidden"</span>);
}
modal.onclick = <span class="hljs-function">(<span class="hljs-params">e</span>)=&gt;</span>{
    <span class="hljs-keyword">if</span>(e.target === modal) modal.classList.add(<span class="hljs-string">"hidden"</span>);
}

<span class="hljs-comment">// Nút thêm/xóa yêu thích</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateFavButton</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> exist = favList.find(<span class="hljs-function"><span class="hljs-params">x</span>=&gt;</span>x.char === currentItem.char);
    btnFav.innerText = exist ? <span class="hljs-string">"Xóa khỏi yêu thích"</span> : <span class="hljs-string">"Thêm yêu thích"</span>;
}
btnFav.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
    <span class="hljs-keyword">const</span> idx = favList.findIndex(<span class="hljs-function"><span class="hljs-params">x</span>=&gt;</span>x.char === currentItem.char);
    <span class="hljs-keyword">if</span>(idx &gt;=<span class="hljs-number">0</span>) favList.splice(idx,<span class="hljs-number">1</span>);
    <span class="hljs-keyword">else</span> favList.push(currentItem);
    localStorage.setItem(<span class="hljs-string">"vn_consonant_fav"</span>, <span class="hljs-built_in">JSON</span>.stringify(favList));
    updateFavButton();
    renderFav();
}

<span class="hljs-comment">// Tab chuyển trang</span>
<span class="hljs-keyword">const</span> tabBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".tab-btn"</span>);
<span class="hljs-keyword">const</span> pages = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".page"</span>);
tabBtns.forEach(<span class="hljs-function"><span class="hljs-params">btn</span>=&gt;</span>{
    btn.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> targetId = btn.dataset.target;
        tabBtns.forEach(<span class="hljs-function"><span class="hljs-params">b</span>=&gt;</span>{
            b.classList.remove(<span class="hljs-string">"tab-active"</span>);
            b.querySelector(<span class="hljs-string">"svg"</span>).classList.add(<span class="hljs-string">"text-gray-500"</span>);
        })
        btn.classList.add(<span class="hljs-string">"tab-active"</span>);
        btn.querySelector(<span class="hljs-string">"svg"</span>).classList.remove(<span class="hljs-string">"text-gray-500"</span>);
        pages.forEach(<span class="hljs-function"><span class="hljs-params">p</span>=&gt;</span>p.classList.add(<span class="hljs-string">"hidden"</span>));
        <span class="hljs-built_in">document</span>.getElementById(targetId).classList.remove(<span class="hljs-string">"hidden"</span>);
    }
})

<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"DOMContentLoaded"</span>, ()=&gt;{
    renderCards();
    renderFav();
})

<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"deviceready"</span>, ()=&gt;{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Cordova sẵn sàng"</span>);
}, <span class="hljs-literal">false</span>);</code></span></pre>


<h4 class="wp-block-heading">方案2-插件播放</h4>



<p class="wp-block-paragraph">通过cordova插件cordova-plugin-media 来播放</p>



<p class="wp-block-paragraph">安装插件</p>


<pre class="wp-block-code"><span><code class="hljs">cordova plugin add cordova-plugin-media</code></span></pre>


<p class="wp-block-paragraph">执行过程</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">D:\cordovademo\hello&gt;cordova plugin add cordova-plugin-media
Installing "cordova-plugin-media" for android
Installing "cordova-plugin-file" for android
Subproject Path: CordovaLib
Subproject Path: app

The Android Persistent storage location now defaults to "Internal". Please check this plugin's README to see if your application needs any changes in its config.xml.

If this is a new application no changes are required.

If this is an update to an existing application that did not specify an "AndroidPersistentFileLocation" you may need to add:

      "<span class="hljs-tag">&lt;<span class="hljs-name">preference</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"AndroidPersistentFileLocation"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Compatibility"</span> /&gt;</span>"

to config.xml in order for the application to find previously stored files.

Adding cordova-plugin-media to package.json</code></span></pre>


<p class="wp-block-paragraph">音频播放函数替换</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> mediaPlayer = <span class="hljs-literal">null</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playAudio</span>(<span class="hljs-params">charName</span>) </span>{
    <span class="hljs-comment">// Dừng file cũ</span>
    <span class="hljs-keyword">if</span>(mediaPlayer){
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = <span class="hljs-literal">null</span>;
    }
    <span class="hljs-comment">// Quan trọng: Cordova Android đường dẫn www/audio/</span>
    <span class="hljs-keyword">const</span> src = <span class="hljs-string">`audio/<span class="hljs-subst">${charName}</span>.mp3`</span>;
    mediaPlayer = <span class="hljs-keyword">new</span> Media(src,
        ()=&gt;{
            <span class="hljs-comment">// Phát xong giải phóng</span>
            mediaPlayer.release();
            mediaPlayer = <span class="hljs-literal">null</span>;
        },
        (err)=&gt;{
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Lỗi phát âm thanh"</span>, err);
        }
    );
    mediaPlayer.play();
}</code></span></pre>


<p class="wp-block-paragraph">替换上面为原来的main.js 里面的方法</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-comment">// Dữ liệu phụ âm tiếng Việt</span>
<span class="hljs-keyword">const</span> consonants = &#91;
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"b"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɓ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bật môi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"c"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/k/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm vòm ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ch"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/tɕ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát vòm"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"d"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɗ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bật răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"đ"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɗ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm đệm răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"g"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɣ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát vòm ngạc"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"gh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɣ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Dùng trước e, i"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"h"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/h/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm thanh hơi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"k"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/k/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"kh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/x/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"l"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/l/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bên răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"m"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/m/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi môi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"n"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/n/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ng"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ŋ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi ngạc mềm"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ngh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ŋ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Dùng trước e,i"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"nh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɲ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi vòm ngạc"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"p"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/p/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc môi không bật"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ph"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/f/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát môi răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"q"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/kw/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Luôn đi với u"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"r"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/r/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm rung răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"s"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/s/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"t"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/t/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"th"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/θ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"v"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/v/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát môi răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"x"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/s/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng lợi"</span>}
];

<span class="hljs-keyword">let</span> favList = <span class="hljs-built_in">JSON</span>.parse(localStorage.getItem(<span class="hljs-string">"vn_consonant_fav"</span>)) || &#91;];
<span class="hljs-keyword">let</span> currentAudio = <span class="hljs-literal">null</span>; <span class="hljs-comment">// 全局音频对象，用于停止上一首</span>

<span class="hljs-keyword">const</span> gridEl = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"consonant-grid"</span>);
<span class="hljs-keyword">const</span> favGridEl = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"favorite-grid"</span>);
<span class="hljs-keyword">const</span> modal = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal"</span>);
<span class="hljs-keyword">const</span> modalChar = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-char"</span>);
<span class="hljs-keyword">const</span> modalPro = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-pronounce"</span>);
<span class="hljs-keyword">const</span> modalDesc = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-desc"</span>);
<span class="hljs-keyword">const</span> btnFav = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"btn-fav"</span>);
<span class="hljs-keyword">const</span> closeModal = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"close-modal"</span>);

<span class="hljs-keyword">let</span> currentItem = <span class="hljs-literal">null</span>;

<span class="hljs-comment">// ====================== HÀM PHÁT MP3 ======================</span>
<span class="hljs-keyword">let</span> mediaPlayer = <span class="hljs-literal">null</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playAudio</span>(<span class="hljs-params">charName</span>) </span>{
    <span class="hljs-comment">// Dừng file cũ</span>
    <span class="hljs-keyword">if</span>(mediaPlayer){
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = <span class="hljs-literal">null</span>;
    }
    <span class="hljs-comment">// Quan trọng: Cordova Android đường dẫn www/audio/</span>
    <span class="hljs-keyword">const</span> src = <span class="hljs-string">`audio/<span class="hljs-subst">${charName}</span>.mp3`</span>;
    mediaPlayer = <span class="hljs-keyword">new</span> Media(src,
        ()=&gt;{
            <span class="hljs-comment">// Phát xong giải phóng</span>
            mediaPlayer.release();
            mediaPlayer = <span class="hljs-literal">null</span>;
        },
        (err)=&gt;{
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Lỗi phát âm thanh"</span>, err);
        }
    );
    mediaPlayer.play();
}

<span class="hljs-comment">// Render thẻ phụ âm</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderCards</span>(<span class="hljs-params"></span>)</span>{
    gridEl.innerHTML = <span class="hljs-string">""</span>;
    consonants.forEach(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.className = <span class="hljs-string">"bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer"</span>;
        div.innerHTML = <span class="hljs-string">`&lt;span class="text-2xl font-bold text-gray-800"&gt;<span class="hljs-subst">${item.char}</span>&lt;/span&gt;`</span>;
        div.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span> {
            playAudio(item.char); <span class="hljs-comment">// Nhấn thẻ phát mp3</span>
            openModal(item);
        };
        gridEl.appendChild(div);
    })
}

<span class="hljs-comment">// Render trang yêu thích</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderFav</span>(<span class="hljs-params"></span>)</span>{
    favGridEl.innerHTML = <span class="hljs-string">""</span>;
    <span class="hljs-keyword">if</span>(favList.length === <span class="hljs-number">0</span>){
        favGridEl.innerHTML = <span class="hljs-string">`&lt;p class="col-span-3 text-center text-gray-400 py-10"&gt;Chưa có từ yêu thích&lt;/p&gt;`</span>;
        <span class="hljs-keyword">return</span>;
    }
    favList.forEach(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.className = <span class="hljs-string">"bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer"</span>;
        div.innerHTML = <span class="hljs-string">`&lt;span class="text-2xl font-bold text-gray-800"&gt;<span class="hljs-subst">${item.char}</span>&lt;/span&gt;`</span>;
        div.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span> {
            playAudio(item.char);
            openModal(item);
        };
        favGridEl.appendChild(div);
    })
}

<span class="hljs-comment">// Mở popup chi tiết</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">openModal</span>(<span class="hljs-params">item</span>)</span>{
    currentItem = item;
    modalChar.innerText = item.char;
    modalPro.innerText = item.pronounce;
    modalDesc.innerText = item.desc;
    updateFavButton();
    modal.classList.remove(<span class="hljs-string">"hidden"</span>);
}

<span class="hljs-comment">// Đóng popup</span>
closeModal.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
    modal.classList.add(<span class="hljs-string">"hidden"</span>);
}
modal.onclick = <span class="hljs-function">(<span class="hljs-params">e</span>)=&gt;</span>{
    <span class="hljs-keyword">if</span>(e.target === modal) modal.classList.add(<span class="hljs-string">"hidden"</span>);
}

<span class="hljs-comment">// Nút thêm/xóa yêu thích</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateFavButton</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> exist = favList.find(<span class="hljs-function"><span class="hljs-params">x</span>=&gt;</span>x.char === currentItem.char);
    btnFav.innerText = exist ? <span class="hljs-string">"Xóa khỏi yêu thích"</span> : <span class="hljs-string">"Thêm yêu thích"</span>;
}
btnFav.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
    <span class="hljs-keyword">const</span> idx = favList.findIndex(<span class="hljs-function"><span class="hljs-params">x</span>=&gt;</span>x.char === currentItem.char);
    <span class="hljs-keyword">if</span>(idx &gt;=<span class="hljs-number">0</span>) favList.splice(idx,<span class="hljs-number">1</span>);
    <span class="hljs-keyword">else</span> favList.push(currentItem);
    localStorage.setItem(<span class="hljs-string">"vn_consonant_fav"</span>, <span class="hljs-built_in">JSON</span>.stringify(favList));
    updateFavButton();
    renderFav();
}

<span class="hljs-comment">// Tab chuyển trang</span>
<span class="hljs-keyword">const</span> tabBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".tab-btn"</span>);
<span class="hljs-keyword">const</span> pages = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".page"</span>);
tabBtns.forEach(<span class="hljs-function"><span class="hljs-params">btn</span>=&gt;</span>{
    btn.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> targetId = btn.dataset.target;
        tabBtns.forEach(<span class="hljs-function"><span class="hljs-params">b</span>=&gt;</span>{
            b.classList.remove(<span class="hljs-string">"tab-active"</span>);
            b.querySelector(<span class="hljs-string">"svg"</span>).classList.add(<span class="hljs-string">"text-gray-500"</span>);
        })
        btn.classList.add(<span class="hljs-string">"tab-active"</span>);
        btn.querySelector(<span class="hljs-string">"svg"</span>).classList.remove(<span class="hljs-string">"text-gray-500"</span>);
        pages.forEach(<span class="hljs-function"><span class="hljs-params">p</span>=&gt;</span>p.classList.add(<span class="hljs-string">"hidden"</span>));
        <span class="hljs-built_in">document</span>.getElementById(targetId).classList.remove(<span class="hljs-string">"hidden"</span>);
    }
})

<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"DOMContentLoaded"</span>, ()=&gt;{
    renderCards();
    renderFav();
})

<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"deviceready"</span>, ()=&gt;{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Cordova sẵn sàng"</span>);
}, <span class="hljs-literal">false</span>);</code></span></pre>


<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.foxdevelop.com/zh/2026/07/29/%e6%92%ad%e6%94%be%e9%9f%b3%e9%a2%91/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>越南语学习例子</title>
		<link>https://www.foxdevelop.com/zh/2026/07/29/%e8%b6%8a%e5%8d%97%e8%af%ad%e5%ad%a6%e4%b9%a0%e4%be%8b%e5%ad%90/</link>
					<comments>https://www.foxdevelop.com/zh/2026/07/29/%e8%b6%8a%e5%8d%97%e8%af%ad%e5%ad%a6%e4%b9%a0%e4%be%8b%e5%ad%90/#respond</comments>
		
		<dc:creator><![CDATA[jack]]></dc:creator>
		<pubDate>Wed, 29 Jul 2026 02:25:27 +0000</pubDate>
				<category><![CDATA[Cordova 教程]]></category>
		<guid isPermaLink="false">https://www.foxdevelop.com/?p=6716</guid>

					<description><![CDATA[下面以一个简单的越南语学习APP为例，界面是越南语的辅音。底 ...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">下面以一个简单的越南语学习APP为例，界面是越南语的辅音。底部是一个个菜单</p>



<p class="wp-block-paragraph">我们使用tailwind作为网页模板。</p>



<p class="wp-block-paragraph">关于tailwind，读者可以学习其他课程。</p>



<p class="wp-block-paragraph">修改index.html 内容如下</p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"vi"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Bảng phụ âm tiếng Việt<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Tailwind 内嵌CDN 离线可用建议打包后替换本地 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tailwindcss.com"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="actionscript">
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: <span class="hljs-string">'#2563eb'</span>,
                        bottomNav: <span class="hljs-string">'#ffffff'</span>,
                        cardBg: <span class="hljs-string">'#f8fafc'</span>
                    }
                }
            }
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/tailwindcss"</span>&gt;</span><span class="css">
        <span class="hljs-keyword">@layer</span> utilities {
            <span class="hljs-selector-class">.safe-bottom</span> {
                <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">4rem</span> + env(safe-area-inset-bottom));
            }
            <span class="hljs-selector-class">.card-shadow</span> {
                <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">8px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0.08</span>);
            }
        }
        <span class="hljs-selector-tag">body</span> {
            <span class="hljs-comment">/* 防止cordova滚动回弹 */</span>
            <span class="hljs-attribute">overscroll-behavior</span>: none;
        }
        <span class="hljs-comment">/* 底部tab激活样式 */</span>
        <span class="hljs-selector-class">.tab-active</span> {
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#2563eb</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100 font-sans text-gray-800"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- 页面容器 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-md mx-auto bg-white min-h-screen relative safe-bottom"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- 顶部标题栏 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sticky top-0 bg-primary text-white px-4 py-3 shadow z-20"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold text-center"</span>&gt;</span>Phụ Âm Tiếng Việt<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- 主内容区域 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4"</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Trang 1: Phụ âm --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"page-consonant"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-base font-medium mb-3 text-gray-600"</span>&gt;</span>Nhấn vào thẻ để xem chi tiết<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"consonant-grid"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-cols-3 gap-3"</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- JS渲染辅音卡片 --&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-comment">&lt;!-- Trang 2: Yêu thích --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"page-favorite"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page hidden mt-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold mb-4"</span>&gt;</span>Danh sách yêu thích<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"favorite-grid"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-cols-3 gap-3"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-span-3 text-center text-gray-400 py-10"</span>&gt;</span>Chưa có từ yêu thích<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-comment">&lt;!-- Trang3: Cài đặt --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"page-setting"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page hidden mt-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold mb-4"</span>&gt;</span>Cài đặt<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-3"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-cardBg p-4 rounded-lg card-shadow"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Ứng dụng học phụ âm tiếng Việt<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-500 mt-1"</span>&gt;</span>Cordova + TailwindCSS<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- ========== 底部导航栏 ========== --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fixed bottom-0 left-1/2 -translate-x-1/2 w-full max-w-md bg-bottomNav border-t border-gray-200 flex z-30"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding-bottom: env(safe-area-inset-bottom);"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tab-btn flex-1 py-3 flex flex-col items-center tab-active"</span> <span class="hljs-attr">data-target</span>=<span class="hljs-string">"page-consonant"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"18"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"18"</span> <span class="hljs-attr">rx</span>=<span class="hljs-string">"2"</span>/&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M3 9h18M9 21V9"</span>/&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs mt-1"</span>&gt;</span>Phụ âm<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tab-btn flex-1 py-3 flex flex-col items-center text-gray-500"</span> <span class="hljs-attr">data-target</span>=<span class="hljs-string">"page-favorite"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"</span>/&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs mt-1"</span>&gt;</span>Yêu thích<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tab-btn flex-1 py-3 flex flex-col items-center text-gray-500"</span> <span class="hljs-attr">data-target</span>=<span class="hljs-string">"page-setting"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"12"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"12"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"3"</span>/&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"</span>/&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs mt-1"</span>&gt;</span>Cài đặt<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- ========== 弹窗 hiển thị chi tiết phụ âm ========== --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"modal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white rounded-xl w-full max-w-sm p-5 relative"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"close-modal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute top-3 right-3 text-gray-400 text-xl"</span>&gt;</span><span class="hljs-symbol">&amp;times;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"modal-char"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-4xl font-bold text-primary text-center py-4"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-2 text-gray-700"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Phiên âm:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"modal-pronounce"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Giải thích:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"modal-desc"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"btn-fav"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full mt-5 bg-primary text-white py-2 rounded-lg"</span>&gt;</span>Thêm yêu thích<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Cordova js (chỉ chạy trên thiết bị thật) --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"cordova.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></code></span></pre>


<p class="wp-block-paragraph">在www/js/main.js 创建main.js 文件</p>



<p class="wp-block-paragraph">内容如下</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-comment">// Dữ liệu phụ âm tiếng Việt</span>
<span class="hljs-keyword">const</span> consonants = &#91;
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"b"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɓ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bật môi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"c"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/k/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm vòm ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ch"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/tɕ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát vòm"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"d"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɗ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bật răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"đ"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɗ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm đệm răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"g"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɣ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát vòm ngạc"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"gh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɣ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Dùng trước e, i"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"h"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/h/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm thanh hơi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"k"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/k/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"kh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/x/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát ngạc cứng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"l"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/l/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm bên răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"m"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/m/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi môi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"n"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/n/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ng"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ŋ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi ngạc mềm"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ngh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ŋ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Dùng trước e,i"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"nh"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/ɲ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm mũi vòm ngạc"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"p"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/p/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc môi không bật"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"ph"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/f/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát môi răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"q"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/kw/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Luôn đi với u"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"r"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/r/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm rung răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"s"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/s/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"t"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/t/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm tắc răng lợi"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"th"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/θ/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"v"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/v/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát môi răng"</span>},
    {<span class="hljs-attr">char</span>: <span class="hljs-string">"x"</span>, <span class="hljs-attr">pronounce</span>: <span class="hljs-string">"/s/"</span>, <span class="hljs-attr">desc</span>: <span class="hljs-string">"Phụ âm xát răng lợi"</span>}
];

<span class="hljs-comment">// Lưu danh sách yêu thích localStorage</span>
<span class="hljs-keyword">let</span> favList = <span class="hljs-built_in">JSON</span>.parse(localStorage.getItem(<span class="hljs-string">"vn_consonant_fav"</span>)) || &#91;];

<span class="hljs-comment">// DOM</span>
<span class="hljs-keyword">const</span> gridEl = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"consonant-grid"</span>);
<span class="hljs-keyword">const</span> favGridEl = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"favorite-grid"</span>);
<span class="hljs-keyword">const</span> modal = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal"</span>);
<span class="hljs-keyword">const</span> modalChar = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-char"</span>);
<span class="hljs-keyword">const</span> modalPro = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-pronounce"</span>);
<span class="hljs-keyword">const</span> modalDesc = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-desc"</span>);
<span class="hljs-keyword">const</span> btnFav = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"btn-fav"</span>);
<span class="hljs-keyword">const</span> closeModal = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"close-modal"</span>);

<span class="hljs-keyword">let</span> currentItem = <span class="hljs-literal">null</span>;

<span class="hljs-comment">// Render thẻ phụ âm</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderCards</span>(<span class="hljs-params"></span>)</span>{
    gridEl.innerHTML = <span class="hljs-string">""</span>;
    consonants.forEach(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.className = <span class="hljs-string">"bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform"</span>;
        div.innerHTML = <span class="hljs-string">`&lt;span class="text-2xl font-bold text-gray-800"&gt;<span class="hljs-subst">${item.char}</span>&lt;/span&gt;`</span>;
        div.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span> openModal(item);
        gridEl.appendChild(div);
    })
}

<span class="hljs-comment">// Render trang yêu thích</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderFav</span>(<span class="hljs-params"></span>)</span>{
    favGridEl.innerHTML = <span class="hljs-string">""</span>;
    <span class="hljs-keyword">if</span>(favList.length === <span class="hljs-number">0</span>){
        favGridEl.innerHTML = <span class="hljs-string">`&lt;p class="col-span-3 text-center text-gray-400 py-10"&gt;Chưa có từ yêu thích&lt;/p&gt;`</span>;
        <span class="hljs-keyword">return</span>;
    }
    favList.forEach(<span class="hljs-function"><span class="hljs-params">item</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.className = <span class="hljs-string">"bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform"</span>;
        div.innerHTML = <span class="hljs-string">`&lt;span class="text-2xl font-bold text-gray-800"&gt;<span class="hljs-subst">${item.char}</span>&lt;/span&gt;`</span>;
        div.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span> openModal(item);
        favGridEl.appendChild(div);
    })
}

<span class="hljs-comment">// Mở popup chi tiết</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">openModal</span>(<span class="hljs-params">item</span>)</span>{
    currentItem = item;
    modalChar.innerText = item.char;
    modalPro.innerText = item.pronounce;
    modalDesc.innerText = item.desc;
    updateFavButton();
    modal.classList.remove(<span class="hljs-string">"hidden"</span>);
}

<span class="hljs-comment">// Đóng popup</span>
closeModal.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
    modal.classList.add(<span class="hljs-string">"hidden"</span>);
}
modal.onclick = <span class="hljs-function">(<span class="hljs-params">e</span>)=&gt;</span>{
    <span class="hljs-keyword">if</span>(e.target === modal) modal.classList.add(<span class="hljs-string">"hidden"</span>);
}

<span class="hljs-comment">// Nút thêm/xóa yêu thích</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateFavButton</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> exist = favList.find(<span class="hljs-function"><span class="hljs-params">x</span>=&gt;</span>x.char === currentItem.char);
    <span class="hljs-keyword">if</span>(exist){
        btnFav.innerText = <span class="hljs-string">"Xóa khỏi yêu thích"</span>;
    }<span class="hljs-keyword">else</span>{
        btnFav.innerText = <span class="hljs-string">"Thêm yêu thích"</span>;
    }
}
btnFav.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
    <span class="hljs-keyword">const</span> idx = favList.findIndex(<span class="hljs-function"><span class="hljs-params">x</span>=&gt;</span>x.char === currentItem.char);
    <span class="hljs-keyword">if</span>(idx &gt;=<span class="hljs-number">0</span>){
        favList.splice(idx,<span class="hljs-number">1</span>);
    }<span class="hljs-keyword">else</span>{
        favList.push(currentItem);
    }
    localStorage.setItem(<span class="hljs-string">"vn_consonant_fav"</span>, <span class="hljs-built_in">JSON</span>.stringify(favList));
    updateFavButton();
    renderFav();
}

<span class="hljs-comment">// ========== Tab chuyển trang ==========</span>
<span class="hljs-keyword">const</span> tabBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".tab-btn"</span>);
<span class="hljs-keyword">const</span> pages = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".page"</span>);
tabBtns.forEach(<span class="hljs-function"><span class="hljs-params">btn</span>=&gt;</span>{
    btn.onclick = <span class="hljs-function"><span class="hljs-params">()</span>=&gt;</span>{
        <span class="hljs-keyword">const</span> targetId = btn.dataset.target;
        <span class="hljs-comment">// Xóa active tất cả tab</span>
        tabBtns.forEach(<span class="hljs-function"><span class="hljs-params">b</span>=&gt;</span>{
            b.classList.remove(<span class="hljs-string">"tab-active"</span>);
            b.querySelector(<span class="hljs-string">"svg"</span>).classList.add(<span class="hljs-string">"text-gray-500"</span>);
        })
        btn.classList.add(<span class="hljs-string">"tab-active"</span>);
        btn.querySelector(<span class="hljs-string">"svg"</span>).classList.remove(<span class="hljs-string">"text-gray-500"</span>);

        <span class="hljs-comment">// Ẩn tất cả trang</span>
        pages.forEach(<span class="hljs-function"><span class="hljs-params">p</span>=&gt;</span>p.classList.add(<span class="hljs-string">"hidden"</span>));
        <span class="hljs-built_in">document</span>.getElementById(targetId).classList.remove(<span class="hljs-string">"hidden"</span>);
    }
})

<span class="hljs-comment">// Khởi chạy</span>
<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"DOMContentLoaded"</span>, ()=&gt;{
    renderCards();
    renderFav();
})

<span class="hljs-comment">// Cordova sẵn sàng (nếu chạy trên điện thoại)</span>
<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"deviceready"</span>, ()=&gt;{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Cordova ready"</span>);
}, <span class="hljs-literal">false</span>);</code></span></pre>


<p class="wp-block-paragraph">重新编译 运行 </p>



<p class="wp-block-paragraph">D:\cordovademo\hello><mark style="background-color:rgba(0, 0, 0, 0);color:#f30c0c" class="has-inline-color">cordova run android</mark></p>



<p class="wp-block-paragraph">APP中看到如下内容</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="461" height="1024" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97-461x1024.jpg" alt="" class="wp-image-6717" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97-461x1024.jpg 461w, https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97-135x300.jpg 135w, https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97-768x1707.jpg 768w, https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97-691x1536.jpg 691w, https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97-922x2048.jpg 922w, https://www.foxdevelop.com/wp-content/uploads/2026/07/8a2e5a99a05ed57850df9841530c0f97.jpg 1080w" sizes="(max-width: 461px) 100vw, 461px" /></figure>



<p class="wp-block-paragraph">卡片可以点击，底部菜单栏可以切换。</p>



<p class="wp-block-paragraph">接下来，我们实现点击其中一个卡片，播放一个小音频。请看下一张</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.foxdevelop.com/zh/2026/07/29/%e8%b6%8a%e5%8d%97%e8%af%ad%e5%ad%a6%e4%b9%a0%e4%be%8b%e5%ad%90/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cordova 命令行工具（CLI）（参考手册）</title>
		<link>https://www.foxdevelop.com/zh/2026/07/29/cordova-%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%b7%a5%e5%85%b7%ef%bc%88cli%ef%bc%89%ef%bc%88%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c%ef%bc%89/</link>
					<comments>https://www.foxdevelop.com/zh/2026/07/29/cordova-%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%b7%a5%e5%85%b7%ef%bc%88cli%ef%bc%89%ef%bc%88%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c%ef%bc%89/#respond</comments>
		
		<dc:creator><![CDATA[jack]]></dc:creator>
		<pubDate>Wed, 29 Jul 2026 02:09:25 +0000</pubDate>
				<category><![CDATA[Cordova 教程]]></category>
		<guid isPermaLink="false">https://www.foxdevelop.com/?p=6713</guid>

					<description><![CDATA[本章是参考手册，不需要全部学习，用到查阅就可以。 CLI 语 ...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">本章是参考手册，不需要全部学习，用到查阅就可以。</p>



<h3 class="wp-block-heading">CLI 语法</h3>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> &lt;<span class="hljs-selector-tag">command</span>&gt; <span class="hljs-selector-attr">&#91;选项]</span> <span class="hljs-selector-tag">--</span> <span class="hljs-selector-attr">&#91;平台专属参数]</span></code></span></pre>


<h3 class="wp-block-heading">全局命令列表</h3>



<p class="wp-block-paragraph"><strong>无需进入 Cordova 项目目录即可执行</strong></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td>create</td><td>创建新项目</td></tr><tr><td>help</td><td>查看指定命令帮助文档</td></tr><tr><td>config</td><td>设置、查询、删除、编辑、列出 Cordova 全局配置项</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">项目命令列表</h3>



<p class="wp-block-paragraph"><strong>必须在有效的 Cordova 项目根目录执行</strong></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td>info</td><td>输出项目环境信息</td></tr><tr><td>requirements</td><td>检测并打印对应平台所需全部依赖环境</td></tr><tr><td>platform</td><td>管理项目平台（Android/iOS/Browser等）</td></tr><tr><td>plugin</td><td>管理项目插件</td></tr><tr><td>prepare</td><td>将资源文件复制到各平台目录，为编译做准备</td></tr><tr><td>compile</td><td>编译指定平台项目</td></tr><tr><td>build</td><td>构建应用（等价 prepare + compile）</td></tr><tr><td>clean</td><td>清理平台编译产生的缓存产物</td></tr><tr><td>run</td><td>运行应用（内部执行 prepare + compile + 部署启动）</td></tr><tr><td>serve</td><td>启动本地 Web 服务器预览网页资源（内部执行 prepare）</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">通用选项（全部 CLI 命令支持）</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>-d / &#8211;verbose</td><td>输出详细日志。如果你在 Node 代码中调用 cordova-cli，可监听 <code>log</code>、<code>warn</code> 事件获取日志：<code>cordova.on('log', ()=&gt;{})</code></td></tr><tr><td>-v / &#8211;version</td><td>打印当前 Cordova CLI 版本</td></tr><tr><td>&#8211;nohooks</td><td>禁止执行钩子脚本（hook），支持正则过滤钩子</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">平台专属参数说明</h3>



<p class="wp-block-paragraph">部分命令支持平台自定义参数（<code>platformOpts</code>）。<br>需要使用 <code>--</code> 作为分隔符：<code>--</code> 之前由 Cordova CLI 解析，<strong><code>--</code> 之后所有参数直接透传给对应原生平台构建工具</strong>。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">CLI 使用示例</h3>



<p class="wp-block-paragraph">演示完整工作流：</p>



<ol class="wp-block-list">
<li>创建项目</li>



<li>安装相机插件</li>



<li>添加 Android 平台</li>



<li>编译、运行 Android</li>



<li>使用 Android 签名参数打正式包</li>
</ol>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 创建项目</span>
cordova create myApp com.foxdevelop.myApp myApp
cd myApp

<span class="hljs-comment"># 添加相机插件</span>
cordova plugin add cordova-plugin-camera

<span class="hljs-comment"># 添加 Android 平台</span>
cordova platform add android

<span class="hljs-comment"># 检测 Android 编译环境依赖</span>
cordova requirements android

<span class="hljs-comment"># 编译 Android（输出详细日志）</span>
cordova build android --verbose

<span class="hljs-comment"># 在设备/模拟器运行 App</span>
cordova run android

<span class="hljs-comment"># Release正式包并传入签名参数</span>
cordova build android --release -- --keystore=<span class="hljs-string">"..\android.keystore"</span> --storePassword=android --alias=mykey</code></span></pre>


<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">各命令详细说明</h3>



<h4 class="wp-block-heading">cordova create</h4>



<p class="wp-block-paragraph">创建 Cordova 项目目录结构。<br>语法：</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">path</span> <span class="hljs-selector-attr">&#91;包ID &#91;应用名称]</span>] <span class="hljs-selector-attr">&#91;选项]</span></code></span></pre>


<h4 class="wp-block-heading">参数</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td>path</td><td>项目文件夹（文件夹不能预先存在，CLI自动创建）</td></tr><tr><td>id</td><td>包标识，默认：<code>org.apache.cordova.hellocordova</code><br>反向域名格式，对应 <code>config.xml</code> 的 widget id；会用于生成 Java 包名，建议合理设置</td></tr><tr><td>name</td><td>应用显示名称，默认：<code>Hello Cordova</code><br>对应 config.xml name，会生成为原生项目类名</td></tr></tbody></table></figure>



<h4 class="wp-block-heading">选项</h4>



<ul class="wp-block-list">
<li><code>--template</code>：使用本地/NPM/GitHub 自定义模板创建项目</li>
</ul>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">myapp</span> <span class="hljs-selector-tag">com</span><span class="hljs-selector-class">.mycompany</span><span class="hljs-selector-class">.myteam</span><span class="hljs-selector-class">.myapp</span> <span class="hljs-selector-tag">MyApp</span></code></span></pre>


<h4 class="wp-block-heading">cordova platform</h4>



<p class="wp-block-paragraph">管理平台：新增、移除、更新、列出平台。增删平台会修改项目 <code>platforms</code> 目录。</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">cordova {platform | platforms} &#91;
    add <span class="hljs-tag">&lt;<span class="hljs-name">平台描述</span>&gt;</span> &#91;...] {--save | link=<span class="hljs-tag">&lt;<span class="hljs-name">路径</span>&gt;</span> } |
    {remove | rm} 平台 &#91;...] {--save}|
    {list | ls}  |
    update ]</code></span></pre>


<h3 class="wp-block-heading">子命令与参数</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>子命令</th><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>add &lt;平台描述&gt;</td><td></td><td>添加平台</td></tr><tr><td></td><td>&#8211;nosave</td><td>安装后<strong>不</strong>将平台信息写入 config.xml / package.json</td></tr><tr><td></td><td>&#8211;link=</td><td>使用本地平台源码时创建软链接，而非复制文件（平台开发调试使用）</td></tr><tr><td>remove &lt;平台&gt;</td><td></td><td>删除平台</td></tr><tr><td></td><td>&#8211;nosave</td><td>删除平台，但<strong>不</strong>移除 config.xml / package.json 内记录</td></tr><tr><td>update &lt;平台&gt;</td><td></td><td>更新平台版本</td></tr><tr><td></td><td>&#8211;save</td><td>更新 config.xml 内记录的平台版本</td></tr><tr><td>list / ls</td><td></td><td>列出所有已安装与可用平台</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">平台描述格式 <code>&lt;platform-spec&gt;</code></h3>


<pre class="wp-block-code"><span><code class="hljs language-php">平台&#91;@版本号] | 本地路径 | Git地址&#91;<span class="hljs-comment">#分支/tag/commit]</span></code></span></pre>


<ul class="wp-block-list">
<li>platform：平台名称 android / ios / browser / electron</li>



<li>version：语义化版本 semver，例如 <code>^13.0.0</code></li>



<li>path：本地平台源码目录 / tar压缩包</li>



<li>url：Git仓库地址</li>



<li>commit-ish：指定分支、tag、commit哈希；不写默认 master</li>
</ul>



<h4 class="wp-block-heading">支持平台</h4>



<p class="wp-block-paragraph"><code>android</code>、<code>browser</code>、<code>electron</code>、<code>ios</code></p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 添加Android+iOS，并写入配置文件</span>
cordova platform add android ios

<span class="hljs-comment"># 指定版本添加Android平台</span>
cordova platform add android@^<span class="hljs-number">13.0</span><span class="hljs-number">.0</span>

<span class="hljs-comment"># 从Git仓库指定tag安装平台</span>
cordova platform add https:<span class="hljs-comment">//github.com/myfork/cordova-android.git#13.0.0</span>

<span class="hljs-comment"># 本地目录安装平台</span>
cordova platform add ../android

<span class="hljs-comment"># 删除Android平台</span>
cordova platform rm android

<span class="hljs-comment"># 查看所有平台</span>
cordova platform ls</code></span></pre>


<h4 class="wp-block-heading">cordova plugin</h4>



<p class="wp-block-paragraph">管理项目插件</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">cordova {plugin | plugins} &#91;
    add <span class="hljs-tag">&lt;<span class="hljs-name">插件描述</span>&gt;</span> &#91;..] {--searchpath=<span class="hljs-tag">&lt;<span class="hljs-name">目录</span>&gt;</span> | --noregistry | --link | --save | --force} |
    {remove | rm} {<span class="hljs-tag">&lt;<span class="hljs-name">插件ID</span>&gt;</span> | <span class="hljs-tag">&lt;<span class="hljs-name">名称</span>&gt;</span>} --save |
    {list | ls}
]</code></span></pre>


<h4 class="wp-block-heading">子命令参数</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>子命令</th><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>add &lt;插件描述&gt;</td><td></td><td>添加插件</td></tr><tr><td></td><td>&#8211;searchpath &lt;目录&gt;</td><td>查找插件时优先检索本地目录，多路径分隔：Windows <code>;</code>、Linux/Mac <code>:</code></td></tr><tr><td></td><td>&#8211;noregistry</td><td>不从npm官方仓库拉取插件</td></tr><tr><td></td><td>&#8211;link</td><td>本地插件目录安装时创建软链接（插件开发调试）</td></tr><tr><td></td><td>&#8211;nosave</td><td>不写入 config.xml / package.json</td></tr><tr><td></td><td>&#8211;force</td><td>强制覆盖冲突文件（6.1版本新增）</td></tr><tr><td>remove</td><td></td><td>删除插件</td></tr><tr><td></td><td>&#8211;nosave</td><td>删除插件但保留配置文件记录</td></tr><tr><td>list / ls</td><td></td><td>列出已安装插件</td></tr></tbody></table></figure>



<h4 class="wp-block-heading">插件描述格式 <code>&lt;plugin-spec&gt;</code></h4>


<pre class="wp-block-code"><span><code class="hljs language-php">&#91;@scope/]插件ID&#91;@版本] | 本地目录 | Git地址&#91;<span class="hljs-comment">#commit]&#91;:子目录]</span></code></span></pre>


<h4 class="wp-block-heading">插件解析优先级（由高到低）</h4>



<ol class="wp-block-list">
<li>命令行显式指定的插件与版本</li>



<li>config.xml / package.json 保存的插件信息</li>



<li>npm上兼容当前项目的最新版本（插件package.json声明cordova依赖）</li>



<li>npm上插件最新版本</li>
</ol>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 安装相机、文件插件，优先从../plugins查找</span>
cordova plugin add cordova-plugin-camera cordova-plugin-file --searchpath ../plugins

<span class="hljs-comment"># 指定版本安装</span>
cordova plugin add cordova-plugin-camera@^<span class="hljs-number">2.0</span><span class="hljs-number">.0</span>

<span class="hljs-comment"># 本地目录安装插件</span>
cordova plugin add ../cordova-plugin-camera

<span class="hljs-comment"># 删除插件</span>
cordova plugin rm cordova-plugin-camera

<span class="hljs-comment"># 查看插件列表</span>
cordova plugin ls</code></span></pre>


<h4 class="wp-block-heading">插件冲突说明</h4>



<p class="wp-block-paragraph">部分插件通过 <code>plugin.xml</code> 的 <code>edit-config</code> 修改原生配置文件。<br>多个插件同时修改同一个XML节点会产生冲突，CLI默认阻止安装。<br>解决方式：</p>



<ol class="wp-block-list">
<li>修改插件 plugin.xml，避免修改同一节点（推荐）</li>



<li>使用 <code>--force</code> 强制安装（<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 风险：会覆盖其他插件修改，可能导致其他插件失效）</li>
</ol>



<h4 class="wp-block-heading">cordova prepare</h4>



<p class="wp-block-paragraph">将 config.xml 配置同步到各平台原生清单文件；复制图标、启动图、插件资源；使项目满足原生SDK编译条件。</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">prepare</span> <span class="hljs-selector-attr">&#91;平台名称...]</span></code></span></pre>


<p class="wp-block-paragraph">不指定平台则全部执行。</p>



<h4 class="wp-block-heading">cordova compile</h4>



<p class="wp-block-paragraph">仅执行编译，<strong>不会执行 prepare</strong>。一般直接使用 <code>build</code>，多用于钩子扩展场景。</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">compile</span> <span class="hljs-selector-attr">&#91;平台...]</span>
    <span class="hljs-selector-attr">&#91;--debug | --release]</span>
    <span class="hljs-selector-attr">&#91;--device | --emulator | --target=&lt;名称&gt;]</span>
    <span class="hljs-selector-attr">&#91;--buildConfig=&lt;文件&gt;]</span>
    <span class="hljs-selector-attr">&#91;-- 平台参数]</span></code></span></pre>


<h4 class="wp-block-heading">cordova build</h4>



<p class="wp-block-paragraph">等价 <code>prepare + compile</code>，构建应用包。</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">build</span> <span class="hljs-selector-attr">&#91;平台...]</span>
    <span class="hljs-selector-attr">&#91;--debug | --release]</span>
    <span class="hljs-selector-attr">&#91;--device | --emulator]</span>
    <span class="hljs-selector-attr">&#91;--buildConfig=&lt;配置文件&gt;]</span>
    <span class="hljs-selector-attr">&#91;-- 平台参数]</span></code></span></pre>


<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>&#8211;debug</td><td>Debug调试包</td></tr><tr><td>&#8211;release</td><td>Release正式发行包</td></tr><tr><td>&#8211;device</td><td>构建适用于真机的包</td></tr><tr><td>&#8211;emulator</td><td>构建模拟器架构包</td></tr><tr><td>&#8211;buildConfig</td><td>指定构建配置文件，默认项目根目录 <code>build.json</code>，常用于签名配置</td></tr></tbody></table></figure>


<pre class="wp-block-code"><span><code class="hljs language-javascript">cordova build android --release --buildConfig=build.json
cordova build android --release -- --keystore=<span class="hljs-string">"app.keystore"</span> --alias=key0</code></span></pre>


<h4 class="wp-block-heading">cordova run</h4>



<p class="wp-block-paragraph">执行 prepare → build → 将应用部署到真机/模拟器并启动。</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-attr">&#91;平台...]</span>
    <span class="hljs-selector-attr">&#91;--list | --debug | --release]</span>
    <span class="hljs-selector-attr">&#91;--noprepare]</span>
    <span class="hljs-selector-attr">&#91;--nobuild]</span>
    <span class="hljs-selector-attr">&#91;--device | --emulator | --target=&lt;名称&gt;]</span>
    <span class="hljs-selector-attr">&#91;--buildConfig=&lt;文件&gt;]</span>
    <span class="hljs-selector-attr">&#91;-- 平台参数]</span></code></span></pre>


<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>&#8211;list</td><td>列出所有可用真机、模拟器设备</td></tr><tr><td>&#8211;noprepare</td><td>跳过prepare（6.2+）</td></tr><tr><td>&#8211;nobuild</td><td>跳过编译，直接部署已有安装包</td></tr><tr><td>&#8211;target</td><td>指定模拟器/设备名称，配合 <code>--list</code> 查询名称</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">示例：</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 列出设备</span>
cordova run android --<span class="hljs-keyword">list</span>
<span class="hljs-comment"># 指定模拟器运行</span>
cordova run android --target=Nexus_5_API_23_x86
<span class="hljs-comment"># 不重新编译，直接运行</span>
cordova run android --nobuild</code></span></pre>


<h4 class="wp-block-heading">cordova emulate</h4>



<p class="wp-block-paragraph"><code>cordova run --emulator</code> 的别名，仅在模拟器启动应用。</p>



<h4 class="wp-block-heading">cordova clean</h4>



<p class="wp-block-paragraph">清理各平台编译缓存产物</p>


<pre class="wp-block-code"><span><code class="hljs">cordova clean android</code></span></pre>


<h4 class="wp-block-heading">cordova requirements</h4>



<p class="wp-block-paragraph">检测平台所需全部环境依赖（JDK、Android SDK、Xcode等）</p>


<pre class="wp-block-code"><span><code class="hljs">cordova requirements android</code></span></pre>


<p class="wp-block-paragraph">所有依赖满足返回退出码0，缺失依赖返回非0，常用于自动化构建环境校验。</p>



<h4 class="wp-block-heading">cordova info</h4>



<p class="wp-block-paragraph">输出完整环境、平台、插件信息，提交bug反馈必备。</p>


<pre class="wp-block-code"><span><code class="hljs">cordova info</code></span></pre>


<h4 class="wp-block-heading">cordova serve</h4>



<p class="wp-block-paragraph">启动本地 Web 服务预览 www 网页资源，默认端口8000</p>


<pre class="wp-block-code"><span><code class="hljs language-php">cordova serve <span class="hljs-number">8080</span>
<span class="hljs-comment"># 访问地址：http://127.0.0.1:8080/android/www</span></code></span></pre>


<h4 class="wp-block-heading">cordova help</h4>



<p class="wp-block-paragraph">查看命令帮助</p>


<pre class="wp-block-code"><span><code class="hljs">cordova help build
cordova build -h</code></span></pre>


<h4 class="wp-block-heading">cordova config</h4>



<p class="wp-block-paragraph">管理 Cordova 全局配置</p>


<pre class="wp-block-code"><span><code class="hljs language-php">cordova config ls                <span class="hljs-comment"># 列出全部配置</span>
cordova config set save-exact <span class="hljs-keyword">true</span>
cordova config get save-exact
cordova config delete save-exact
cordova config edit</code></span></pre>


<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.foxdevelop.com/zh/2026/07/29/cordova-%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%b7%a5%e5%85%b7%ef%bc%88cli%ef%bc%89%ef%bc%88%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>目录结构</title>
		<link>https://www.foxdevelop.com/zh/2026/07/29/%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84/</link>
					<comments>https://www.foxdevelop.com/zh/2026/07/29/%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84/#respond</comments>
		
		<dc:creator><![CDATA[jack]]></dc:creator>
		<pubDate>Wed, 29 Jul 2026 02:02:03 +0000</pubDate>
				<category><![CDATA[Cordova 教程]]></category>
		<guid isPermaLink="false">https://www.foxdevelop.com/?p=6710</guid>

					<description><![CDATA[通过 Cordova CLI 创建的项目，默认拥有如下目录结 ...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">通过 Cordova CLI 创建的项目，默认拥有如下目录结构：</p>


<pre class="wp-block-code"><span><code class="hljs">myapp/
├── config.xml
├── node_modules/
├── package.json
├── platforms/
├── plugins/
└── www</code></span></pre>


<h3 class="wp-block-heading">package.json</h3>



<p class="wp-block-paragraph">清单文件，声明项目用到的 JavaScript 包依赖，包含 Cordova、已添加的平台以及所有安装的插件。插件配置变量同样保存在此文件中。</p>



<p class="wp-block-paragraph">你的项目可能已经存在自带依赖的 <code>package.json</code>（例如前端框架生成）。Cordova 只会向文件追加自身所需配置，不会干扰其他工具与现有依赖。</p>


<pre class="wp-block-code"><span><code class="hljs language-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"com.foxdevelop.hello"</span>,
  <span class="hljs-attr">"displayName"</span>: <span class="hljs-string">"HelloWorld"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"A sample Apache Cordova application that responds to the deviceready event."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-attr">"keywords"</span>: &#91;
    <span class="hljs-string">"ecosystem:cordova"</span>
  ],
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Apache Cordova Team"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"Apache-2.0"</span>,
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"cordova-android"</span>: <span class="hljs-string">"^15.1.0"</span>,
    <span class="hljs-attr">"cordova-plugin-camera"</span>: <span class="hljs-string">"^8.0.0"</span>
  },
  <span class="hljs-attr">"cordova"</span>: {
    <span class="hljs-attr">"platforms"</span>: &#91;
      <span class="hljs-string">"android"</span>
    ],
    <span class="hljs-attr">"plugins"</span>: {
      <span class="hljs-attr">"cordova-plugin-camera"</span>: {
        <span class="hljs-attr">"ANDROIDX_CORE_VERSION"</span>: <span class="hljs-string">"1.6.+"</span>
      }
    }
  }
}
</code></span></pre>


<ol class="wp-block-list">
<li><code>"name": "com.foxdevelop.hello"</code> npm 包名称。在 Cordova 项目中，一般和 config.xml 的 widget id 保持一致。</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">注意：这<strong>不是</strong> Android/iOS 原生包名，原生包名依然由 <code>config.xml</code> 的 <code>id</code> 控制。</p>
</blockquote>



<ol start="2" class="wp-block-list">
<li><code>"displayName": "HelloWorld"</code> 友好显示名称，对应 App 应用名，和 config.xml 内 <code>&lt;name></code> 保持对应。</li>



<li><code>"version": "1.0.0"</code> 项目版本号，遵循语义化版本 <code>主版本.次版本.修订号</code>，建议和 config.xml 的 version 统一。</li>



<li><code>"description": "A sample Apache Cordova application that responds to the deviceready event."</code> 项目描述，元信息，用于 npm 展示，不影响 APP 打包运行。</li>



<li><code>"main": "index.js"</code> npm 规范入口文件。<strong>Cordova 几乎不会使用这个字段</strong>，属于模板自动生成，可以忽略。Cordova APP 真正网页入口是 <code>www/index.html</code>（config.xml 控制）。</li>



<li><code>"scripts": { "test": "echo \"Error: no test specified\" &amp;&amp; exit 1" }</code> npm 自定义脚本。模板默认只有 test，没有配置构建、打包快捷命令。 你后续可以自行扩展，例如：</li>
</ol>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-string">"build-android"</span>: <span class="hljs-string">"cordova build android"</span>
}
</code></span></pre>


<ol start="7" class="wp-block-list">
<li><code>"keywords": ["ecosystem:cordova"]</code> npm 搜索标签。<code>ecosystem:cordova</code> 标记该项目为 Cordova 生态工程，仅用于 npm 检索，无运行作用。</li>



<li><code>"author": "Apache Cordova Team"</code> 作者信息，纯元数据。</li>



<li><code>"license": "Apache-2.0"</code> 开源协议：Apache 2.0。Cordova 官方默认协议。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">config.xml</h3>



<p class="wp-block-paragraph">存放 Cordova 应用的各项偏好设置与配置项，用来自定义项目运行行为。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">参考文档：config.xml 官方参考手册</p>
</blockquote>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="hljs-meta">&lt;?xml version='1.0' encoding='utf-8'?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">widget</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"com.foxdevelop.hello"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/ns/widgets"</span> <span class="hljs-attr">xmlns:cdv</span>=<span class="hljs-string">"http://cordova.apache.org/ns/1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>HelloWorld<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>
        A sample Apache Cordova application that responds to the deviceready event.
    <span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">author</span> <span class="hljs-attr">email</span>=<span class="hljs-string">"dev@cordova.apache.org"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cordova.apache.org"</span>&gt;</span>
        Apache Cordova Team
    <span class="hljs-tag">&lt;/<span class="hljs-name">author</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">content</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.html"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">allow-intent</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://*/*"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">allow-intent</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://*/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">widget</span>&gt;</span>
</code></span></pre>


<p class="wp-block-paragraph"><code>id="com.foxdevelop.hello"</code><strong>应用唯一包名（Bundle ID）</strong></p>



<ul class="wp-block-list">
<li>Android：应用包名 <code>applicationId</code></li>



<li>iOS：Bundle Identifier</li>
</ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">上架商店不能随便修改！修改后视为全新应用。规范：反向域名 <code>公司域名.项目名</code></p>
</blockquote>



<p class="wp-block-paragraph"><code>version="1.0.0"</code> 应用版本号（语义化版本 major.minor.patch），打包后对应 App 版本。</p>



<p class="wp-block-paragraph"><code>&lt;name&gt;HelloWorld&lt;/name&gt;</code></p>



<p class="wp-block-paragraph">应用显示名称，手机桌面图标下方展示的 App 名字。</p>



<p class="wp-block-paragraph"><code>&lt;description&gt;</code></p>



<p class="wp-block-paragraph">应用描述信息，部分平台打包、商店信息会读取。</p>



<p class="wp-block-paragraph"><code>&lt;author&gt;</code></p>



<p class="wp-block-paragraph">开发者信息：邮箱、官网、作者名称，仅元数据，不影响运行。</p>



<p class="wp-block-paragraph"><code>&lt;content src="index.html" /&gt;</code></p>



<p class="wp-block-paragraph"><strong><mark style="background-color:#03c4eb" class="has-inline-color">最重要配置</mark></strong></p>



<p class="wp-block-paragraph">Cordova APP 启动加载的首页。</p>



<p class="wp-block-paragraph">程序打开后，容器直接加载 <code>www/index.html</code>。</p>



<p class="wp-block-paragraph">你可以修改为 <code>src="pages/home.html"</code> 变更入口页面。</p>



<p class="wp-block-paragraph"><code>&lt;allow-intent></code></p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">allow-intent</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://*/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">allow-intent</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://*/*"</span> /&gt;</span>
</code></span></pre>


<h3 class="wp-block-heading">作用：允许 App 唤起外部浏览器打开网页</h3>



<p class="wp-block-paragraph"><code>allow-intent</code> ≠ 页面内允许跨域访问！</p>



<p class="wp-block-paragraph">两者极易混淆：</p>



<ol class="wp-block-list">
<li><strong>allow-intent</strong>：点击链接 <code>target="_blank"</code>，跳转<strong>系统浏览器</strong>，允许跳转哪些网址。 当前配置：所有 http/https 网址都允许唤起外部浏览器。</li>



<li><strong>access origin</strong>：控制 <strong>App 内部 webview</strong> 是否可以请求外部接口（跨域），这份配置里<strong>缺失</strong>！</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 常见坑：</p>



<p class="wp-block-paragraph">如果你网页内 AJAX 请求接口报错跨域，只写 allow-intent 没用，必须新增：</p>
</blockquote>



<p class="wp-block-paragraph">xml</p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="hljs-comment">&lt;!-- 允许webview内访问所有https接口 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">access</span> <span class="hljs-attr">origin</span>=<span class="hljs-string">"*"</span> /&gt;</span></code></span></pre>


<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">www/</h3>



<p class="wp-block-paragraph">存放项目网页资源：HTML、CSS、JavaScript 以及各类静态资源文件。</p>



<p class="wp-block-paragraph">作为 Cordova 应用开发者，绝大多数业务代码和资源都放在此目录。执行 <code>cordova prepare</code> 命令后，该目录文件会被复制到各个平台对应的 www 目录中。</p>



<p class="wp-block-paragraph">源码目录 <code>www</code> 会被同步复制至各平台子目录，示例路径：</p>



<p class="wp-block-paragraph"><code>platforms/ios/www</code> 或 <code>platforms/android/assets/www</code>。</p>



<p class="wp-block-paragraph">CLI 会持续从源码 <code>www</code> 目录覆盖平台目录内文件，<strong>只能编辑根目录 www 下的文件，不要直接修改 platforms 内的文件</strong>。</p>



<p class="wp-block-paragraph">如果你使用前端构建工具，应当配置构建产物输出到 <code>www</code> 文件夹；若直接在 www 内开发源码，建议将此目录纳入版本控制。</p>



<h3 class="wp-block-heading">node_modules/</h3>



<p class="wp-block-paragraph">存放从 npm 仓库下载的所有 JavaScript 依赖包，包括 Cordova、配套工具，以及 <code>package.json</code> 声明的项目依赖。</p>



<p class="wp-block-paragraph">执行 <code>cordova platform add</code>、<code>cordova plugin add</code> 添加平台 / 插件时，对应的资源会从 npmjs 仓库拉取并下载至 <code>node_modules/</code>。</p>



<p class="wp-block-paragraph">随后 Cordova 会从该目录复制平台、插件源码到对应位置，保证程序正常运行。</p>



<p class="wp-block-paragraph">该目录同时包含 <code>cordova prepare</code>、<code>cordova build</code> 构建各平台所需脚本。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 重要：<code>node_modules</code> 是原始依赖源码目录，禁止手动修改里面任何文件；同时该目录<strong>不要提交到版本控制系统</strong>。</p>
</blockquote>



<p class="wp-block-paragraph">更多信息查阅 npmjs 目录规范文档。</p>



<h3 class="wp-block-heading">platforms/</h3>



<p class="wp-block-paragraph">存放你添加到项目中的各个平台源码与构建脚本。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 警告：使用 CLI 构建应用时，除非你充分了解原理、或者官方文档明确说明，否则不要修改 <code>/platforms/</code> 内任意文件。</p>



<p class="wp-block-paragraph">项目构建准备、重装插件时，该目录下的文件会被频繁覆盖重写。</p>
</blockquote>



<h3 class="wp-block-heading">plugins/</h3>



<p class="wp-block-paragraph">插件临时中转目录：已安装插件会先复制到此目录，之后再部署到各个平台目录。</p>



<p class="wp-block-paragraph">该目录<strong>不建议提交到版本控制</strong>。</p>



<p class="wp-block-paragraph">如果你开发了自研、内部定制或者修改过的插件，不要存放在 plugins 目录中。</p>



<h4 class="wp-block-heading">版本控制规范</h4>



<p class="wp-block-paragraph">推荐<strong>不要将 platforms/、plugins/ 纳入版本管理</strong>，二者属于构建产物。</p>



<p class="wp-block-paragraph">已安装的平台与插件信息会自动记录在 <code>package.json</code>。执行 <code>cordova prepare</code> 时，程序会自动下载对应的平台与插件。</p>



<h4 class="wp-block-heading">可选目录</h4>



<h4 class="wp-block-heading">merges/</h4>



<p class="wp-block-paragraph">通过 CLI 创建 Cordova 项目时，默认不会生成 merges 目录。官方<strong>不推荐使用该目录，但仍保留兼容支持</strong>。</p>



<p class="wp-block-paragraph">该目录下子文件夹用于存放<strong>平台专属网页资源</strong>（HTML、CSS、JS）。执行 prepare 阶段时，资源会部署到对应原生项目目录。</p>



<p class="wp-block-paragraph">放在 merges/ 中的文件，会<strong>覆盖 www 目录下同名文件（对应平台生效）</strong>。</p>



<p class="wp-block-paragraph">举个示例目录结构：</p>



<p class="wp-block-paragraph">plaintext</p>


<pre class="wp-block-code"><span><code class="hljs">myapp/
├── merges
│   ├── android/
│   │   └── android.js
│   └── ios/
│       └── app.js
└── www/
    └── app.js
</code></span></pre>


<p class="wp-block-paragraph">编译 Android、iOS 项目后：</p>



<ul class="wp-block-list">
<li>Android 应用：包含 <code>app.js</code>（来自 www） + <code>android.js</code></li>



<li>iOS 应用：仅存在 <code>app.js</code>，文件来源是 <code>merges/ios/app.js</code>，覆盖了 www 下通用版 <code>app.js</code></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.foxdevelop.com/zh/2026/07/29/%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>第一个Cordova项目</title>
		<link>https://www.foxdevelop.com/zh/2026/07/28/6619/</link>
					<comments>https://www.foxdevelop.com/zh/2026/07/28/6619/#respond</comments>
		
		<dc:creator><![CDATA[jack]]></dc:creator>
		<pubDate>Tue, 28 Jul 2026 09:36:15 +0000</pubDate>
				<category><![CDATA[Cordova 教程]]></category>
		<guid isPermaLink="false">https://www.foxdevelop.com/?p=6619</guid>

					<description><![CDATA[上节课，我们安装cordova成功了，本节课我们来使用CLI ...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">上节课，我们安装cordova成功了，本节课我们来使用CLI命令来创建项目</p>



<p class="wp-block-paragraph">一、创建 Cordova 项目</p>



<p class="wp-block-paragraph">打开终端，进入你想要存放项目的文件夹，执行创建命令：</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">hello</span> <span class="hljs-selector-tag">com</span><span class="hljs-selector-class">.foxdevelop</span><span class="hljs-selector-class">.hello</span> <span class="hljs-selector-tag">HelloWorld</span></code></span></pre>


<p class="wp-block-paragraph">参数说明：</p>



<ol class="wp-block-list">
<li><code>hello</code>：项目文件夹名称</li>



<li><code>com.example.hello</code>：应用包名（安卓唯一标识，上线必须修改）</li>



<li><code>HelloWorld</code>：应用显示名称</li>
</ol>



<p class="wp-block-paragraph">下面是正式例子，在d盘创建了一个目录，然后在目录里面创建项目</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">C</span>:\<span class="hljs-selector-tag">Users</span>&gt;<span class="hljs-selector-tag">d</span>:

<span class="hljs-selector-tag">D</span>:\&gt;<span class="hljs-selector-tag">mkdir</span> <span class="hljs-selector-tag">cordovademo</span>

<span class="hljs-selector-tag">D</span>:\&gt;<span class="hljs-selector-tag">cd</span> <span class="hljs-selector-tag">cordovademo</span>

<span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">cordovademo</span>&gt;<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">hello</span> <span class="hljs-selector-tag">com</span><span class="hljs-selector-class">.foxdevelop</span><span class="hljs-selector-class">.hello</span> <span class="hljs-selector-tag">HelloWorld</span>
<span class="hljs-selector-tag">Creating</span> <span class="hljs-selector-tag">a</span> <span class="hljs-selector-tag">new</span> <span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">project</span>.</code></span></pre>


<figure class="wp-block-image size-full"><img decoding="async" width="378" height="34" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-319.png" alt="" class="wp-image-6620" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-319.png 378w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-319-300x27.png 300w" sizes="(max-width: 378px) 100vw, 378px" /></figure>



<p class="wp-block-paragraph">然后在D盘中，就可以看到cordovademo目录，里面有一个hello的子目录。</p>



<p class="wp-block-paragraph">核心源码目录：<code>hello/www/</code></p>



<p class="wp-block-paragraph">入口页面：<code>www/index.html</code></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">不要修改 <code>platforms</code> 目录下任何文件，打包时会被覆盖！</p>
</blockquote>



<p class="wp-block-paragraph">二、进入项目，添加平台</p>


<pre class="wp-block-code"><span><code class="hljs">cd hello</code></span></pre>


<p class="wp-block-paragraph">添加平台</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 安卓平台（Windows可用）</span>
cordova platform add android

<span class="hljs-comment"># ios平台【Windows无法编译！仅Mac电脑支持】</span>
<span class="hljs-comment"># cordova platform add ios</span></code></span></pre>


<p class="wp-block-paragraph">执行记录</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">cordovademo</span>&gt;<span class="hljs-selector-tag">cd</span> <span class="hljs-selector-tag">hello</span>

<span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">cordovademo</span>\<span class="hljs-selector-tag">hello</span>&gt;<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">platform</span> <span class="hljs-selector-tag">add</span> <span class="hljs-selector-tag">android</span>
<span class="hljs-selector-tag">Using</span> <span class="hljs-selector-tag">cordova-fetch</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">cordova-android</span>
<span class="hljs-selector-tag">Adding</span> <span class="hljs-selector-tag">android</span> <span class="hljs-selector-tag">project</span>...
<span class="hljs-selector-tag">Creating</span> <span class="hljs-selector-tag">Cordova</span> <span class="hljs-selector-tag">project</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">Android</span> <span class="hljs-selector-tag">platform</span>:
        <span class="hljs-selector-tag">Path</span>: <span class="hljs-selector-tag">platforms</span>\<span class="hljs-selector-tag">android</span>
        <span class="hljs-selector-tag">Package</span>: <span class="hljs-selector-tag">com</span><span class="hljs-selector-class">.foxdevelop</span><span class="hljs-selector-class">.hello</span>
        <span class="hljs-selector-tag">Name</span>: <span class="hljs-selector-tag">HelloWorld</span>
        <span class="hljs-selector-tag">Activity</span>: <span class="hljs-selector-tag">MainActivity</span>
        <span class="hljs-selector-tag">Android</span> <span class="hljs-selector-tag">Target</span> <span class="hljs-selector-tag">SDK</span>: <span class="hljs-selector-tag">android-36</span>
        <span class="hljs-selector-tag">Android</span> <span class="hljs-selector-tag">Compile</span> <span class="hljs-selector-tag">SDK</span>: 36
<span class="hljs-selector-tag">Subproject</span> <span class="hljs-selector-tag">Path</span>: <span class="hljs-selector-tag">CordovaLib</span>
<span class="hljs-selector-tag">Subproject</span> <span class="hljs-selector-tag">Path</span>: <span class="hljs-selector-tag">app</span>
<span class="hljs-selector-tag">Android</span> <span class="hljs-selector-tag">project</span> <span class="hljs-selector-tag">created</span> <span class="hljs-selector-tag">with</span> <span class="hljs-selector-tag">cordova-android</span><span class="hljs-keyword">@15</span>.1.0

<span class="hljs-attribute">D:</span>\cordovademo\hello&gt;cordova platform ls
Installed <span class="hljs-attribute">platforms:</span>
  android <span class="hljs-number">15.1</span>.<span class="hljs-number">0</span>
Available <span class="hljs-attribute">platforms:</span>
  browser
  electron</code></span></pre>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="491" height="330" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-320.png" alt="" class="wp-image-6621" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-320.png 491w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-320-300x202.png 300w" sizes="auto, (max-width: 491px) 100vw, 491px" /></figure>



<p class="wp-block-paragraph">三、环境前置检查（重要）</p>


<pre class="wp-block-code"><span><code class="hljs">cordova requirements</code></span></pre>


<p class="wp-block-paragraph">检查你的系统是否已经配置好了环境，例如安装JDK等等。</p>


<pre class="wp-block-code"><span><code class="hljs language-php">D:\cordovademo\hello&gt;cordova requirements
Android SDK is missing cmdline-tools directory.

Requirements check results <span class="hljs-keyword">for</span> android:
Java JDK: installed <span class="hljs-number">11.0</span><span class="hljs-number">.31</span>
Android SDK: installed <span class="hljs-keyword">true</span>
Android target: not installed
Command failed with <span class="hljs-keyword">exit</span> code <span class="hljs-number">1</span>: avdmanager <span class="hljs-keyword">list</span> target
<span class="hljs-string">'avdmanager'</span> �����ڲ����ⲿ���Ҳ���ǿ����еĳ���
���������ļ���
Gradle: not installed
Could not find an installed version of Gradle either in Android Studio,
<span class="hljs-keyword">or</span> on your system to install the gradle wrapper. Please <span class="hljs-keyword">include</span> gradle
in your path, <span class="hljs-keyword">or</span> install Android Studio
Some of requirements check failed</code></span></pre>


<p class="wp-block-paragraph">正常是</p>


<pre class="wp-block-code"><span><code class="hljs">Java JDK: installed
Android SDK: installed
Gradle: installed</code></span></pre>


<p class="wp-block-paragraph">但是我这里Gradle: not installed</p>



<p class="wp-block-paragraph">如果读者，没安装JDK，则需要安装JDK，关于JDK的安装，可以参考其他课程，</p>



<p class="wp-block-paragraph">我这里安装的jdk是11的版本</p>



<p class="wp-block-paragraph">作者本机出现如下</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">D:\cordovademo\hello&gt;cordova build android
Checking Java JDK and Android SDK versions
ANDROID_HOME=<span class="hljs-literal">undefined</span> (recommended setting)
ANDROID_SDK_ROOT=<span class="hljs-literal">undefined</span> (DEPRECATED)
Android SDK is missing cmdline-tools directory.
Using Android SDK: D:\Sortware\ADB
Could not find an installed version <span class="hljs-keyword">of</span> Gradle either <span class="hljs-keyword">in</span> Android Studio,
or on your system to install the gradle wrapper. Please include gradle
<span class="hljs-keyword">in</span> your path, or install Android Studio</code></span></pre>


<p class="wp-block-paragraph">读者需要安装android studio 里面包含sdk</p>



<p class="wp-block-paragraph">关于android studio，可以参考其他课程</p>



<p class="wp-block-paragraph">打开你的Android studio  顶部菜单：<code>Tools</code> → <code>SDK Manager</code> 然后 找到下面的 sdk 查看你的sdk路径</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="982" height="735" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-321.png" alt="" class="wp-image-6625" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-321.png 982w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-321-300x225.png 300w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-321-768x575.png 768w" sizes="auto, (max-width: 982px) 100vw, 982px" /></figure>



<p class="wp-block-paragraph">然后设置环境变量</p>



<p class="wp-block-paragraph">变量名：ANDROID_HOME<br>变量值：C:\Users\xxxxx\AppData\Local\Android\Sdk</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="472" height="30" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-324.png" alt="" class="wp-image-6628" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-324.png 472w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-324-300x19.png 300w" sizes="auto, (max-width: 472px) 100vw, 472px" /></figure>



<p class="wp-block-paragraph">除了创建变量值，还要加到path中</p>



<p class="wp-block-paragraph">%ANDROID_HOME%\platform-tools<br>%ANDROID_HOME%\cmdline-tools\latest\bin</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="388" height="444" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-325.png" alt="" class="wp-image-6629" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-325.png 388w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-325-262x300.png 262w" sizes="auto, (max-width: 388px) 100vw, 388px" /></figure>



<p class="has-medium-font-size wp-block-paragraph"><mark style="background-color:#03c4eb" class="has-inline-color">Android Studio 安装命令行工具</mark></p>



<p class="wp-block-paragraph">打开 Android Studio → <code>Tools</code> → <code>SDK Manager</code></p>



<p class="wp-block-paragraph">切换到 <strong>SDK Tools</strong> 标签</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="983" height="736" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-322.png" alt="" class="wp-image-6626" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-322.png 983w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-322-300x225.png 300w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-322-768x575.png 768w" sizes="auto, (max-width: 983px) 100vw, 983px" /></figure>



<h3 class="wp-block-heading">下载 Gradle 二进制包</h3>



<p class="wp-block-paragraph">官网下载：<a href="https://link.wtturl.cn/?target=https%3A%2F%2Fgradle.org%2Freleases%2F&amp;scene=im&amp;aid=582478&amp;lang=zh" target="_blank" rel="noreferrer noopener">https://gradle.org/releases/</a></p>



<p class="wp-block-paragraph">解压到路径：<code>C:\gradle\gradle-8.5</code></p>



<p class="wp-block-paragraph"><a href="https://gradle.org/releases/">Gradle by Develocity | Releases</a></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="697" height="241" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-323.png" alt="" class="wp-image-6627" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-323.png 697w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-323-300x104.png 300w" sizes="auto, (max-width: 697px) 100vw, 697px" /></figure>



<h3 class="wp-block-heading">配置系统环境变量</h3>



<p class="wp-block-paragraph">变量名：GRADLE_HOME<br>变量值：C:\gradle-8.7</p>



<p class="wp-block-paragraph">新建【系统变量】</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="319" height="29" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-326.png" alt="" class="wp-image-6630" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-326.png 319w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-326-300x27.png 300w" sizes="auto, (max-width: 319px) 100vw, 319px" /></figure>



<p class="wp-block-paragraph">配置到Path中</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="301" height="68" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-327.png" alt="" class="wp-image-6631"/></figure>



<p class="wp-block-paragraph">验证是否安装成功</p>



<p class="wp-block-paragraph">打开cmd </p>


<pre class="wp-block-code"><span><code class="hljs">gradle -v</code></span></pre>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="610" height="375" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-328.png" alt="" class="wp-image-6632" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-328.png 610w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-328-300x184.png 300w" sizes="auto, (max-width: 610px) 100vw, 610px" /></figure>



<p class="wp-block-paragraph">再次测试，出现下面 则表示成功配置</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">cordovademo</span>\<span class="hljs-selector-tag">hello</span>&gt;<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">requirements</span>

<span class="hljs-selector-tag">Requirements</span> <span class="hljs-selector-tag">check</span> <span class="hljs-selector-tag">results</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">android</span>:
<span class="hljs-selector-tag">Java</span> <span class="hljs-selector-tag">JDK</span>: <span class="hljs-selector-tag">installed</span> 11<span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.31</span>
<span class="hljs-selector-tag">Android</span> <span class="hljs-selector-tag">SDK</span>: <span class="hljs-selector-tag">installed</span> <span class="hljs-selector-tag">true</span>
<span class="hljs-selector-tag">Android</span> <span class="hljs-selector-tag">target</span>: <span class="hljs-selector-tag">not</span> <span class="hljs-selector-tag">installed</span>
<span class="hljs-selector-tag">No</span> <span class="hljs-selector-tag">android</span> <span class="hljs-selector-tag">targets</span> (<span class="hljs-selector-tag">SDKs</span>) <span class="hljs-selector-tag">installed</span>!
<span class="hljs-selector-tag">Gradle</span>: <span class="hljs-selector-tag">installed</span> <span class="hljs-selector-tag">C</span>:\<span class="hljs-selector-tag">gradle-8</span><span class="hljs-selector-class">.7</span>\<span class="hljs-selector-tag">bin</span>\<span class="hljs-selector-tag">gradle</span><span class="hljs-selector-class">.BAT</span>
<span class="hljs-selector-tag">Some</span> <span class="hljs-selector-tag">of</span> <span class="hljs-selector-tag">requirements</span> <span class="hljs-selector-tag">check</span> <span class="hljs-selector-tag">failed</span></code></span></pre>


<h4 class="wp-block-heading">问题2 JDK版本</h4>



<p class="wp-block-paragraph">小编使用cordova run android 命令后报如下错误</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">D:\cordovademo\hello&gt;cordova run android
Checking Java JDK and Android SDK versions
ANDROID_HOME=C:\Users\maiwu\AppData\Local\Android\Sdk (recommended setting)
ANDROID_SDK_ROOT=<span class="hljs-literal">undefined</span> (DEPRECATED)

BUILD SUCCESSFUL <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>sCUTING &#91;<span class="hljs-number">91</span>ms]
<span class="hljs-number">1</span> actionable task: <span class="hljs-number">1</span> up-to-date
Subproject Path: CordovaLib
Subproject Path: app

&#91;Incubating] Problems report is available at: file:<span class="hljs-comment">///D:/cordovademo/hello/platforms/android/build/reports/problems/problems-report.html</span>

FAILURE: Build failed <span class="hljs-keyword">with</span> an exception.

* Where:
Build file <span class="hljs-string">'D:\cordovademo\hello\platforms\android\app\build.gradle'</span> line: <span class="hljs-number">20</span>

* What went wrong:
A problem occurred evaluating project <span class="hljs-string">':app'</span>.
&gt; Failed to apply plugin <span class="hljs-string">'com.android.internal.application'</span>.
   &gt; Android Gradle plugin requires Java <span class="hljs-number">17</span> to run. You are currently using Java <span class="hljs-number">11.</span>
      Your current JDK is located <span class="hljs-keyword">in</span> C:\Program Files\Zulu\zulu<span class="hljs-number">-11</span>
      You can <span class="hljs-keyword">try</span> some <span class="hljs-keyword">of</span> the following options:
       - changing the IDE settings.
       - changing the JAVA_HOME environment variable.
       - changing <span class="hljs-string">`org.gradle.java.home`</span> <span class="hljs-keyword">in</span> <span class="hljs-string">`gradle.properties`</span>.

* Try:
&gt; Run <span class="hljs-keyword">with</span> --stacktrace option to <span class="hljs-keyword">get</span> the stack trace.
&gt; Run with --info or --debug option to <span class="hljs-keyword">get</span> more log output.
&gt; Run with --scan to <span class="hljs-keyword">get</span> full insights.
&gt; Get more help at https://help.gradle.org.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.14.2/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD FAILED in 2s
Command failed with exit code 1: D:\cordovademo\hello\platforms\android\tools\gradlew.bat cdvBuildDebug</code></span></pre>


<p class="wp-block-paragraph">小编当前环境 <strong>JDK11</strong>，但当前 Cordova Android 平台搭配的 AGP (Android Gradle Plugin) <strong>强制要求 JDK17</strong>，构建直接失败。</p>



<p class="wp-block-paragraph">小编用的是<strong>Zulu JDK 1</strong>1 所以本次更新到<strong>Zulu JDK 17</strong></p>



<p class="wp-block-paragraph">请到这里下载 <a href="https://www.azul.com/downloads/?version=java-17-lts&amp;package=jdk#zulu">Java 8, 11, 17, 21, 25 Download for Linux, Windows and macOS</a></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="183" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-329-1024x183.png" alt="" class="wp-image-6701" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-329-1024x183.png 1024w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-329-300x53.png 300w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-329-768x137.png 768w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-329.png 1167w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">下载的是一个msi 然后点击安装 安装的勾选下面这些，让安装包自动配置javahome path这些</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="497" height="388" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-330.png" alt="" class="wp-image-6702" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-330.png 497w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-330-300x234.png 300w" sizes="auto, (max-width: 497px) 100vw, 497px" /></figure>



<p class="wp-block-paragraph">打开CMD检查一下java</p>



<p class="wp-block-paragraph">java -version</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">openjdk</span> <span class="hljs-selector-tag">version</span> "17<span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.20</span>" 2026<span class="hljs-selector-tag">-07-21</span> <span class="hljs-selector-tag">LTS</span>
<span class="hljs-selector-tag">OpenJDK</span> <span class="hljs-selector-tag">Runtime</span> <span class="hljs-selector-tag">Environment</span> <span class="hljs-selector-tag">Zulu17</span><span class="hljs-selector-class">.68</span>+17<span class="hljs-selector-tag">-CA</span> (<span class="hljs-selector-tag">build</span> 17<span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.20</span>+8<span class="hljs-selector-tag">-LTS</span>)
<span class="hljs-selector-tag">OpenJDK</span> 64<span class="hljs-selector-tag">-Bit</span> <span class="hljs-selector-tag">Server</span> <span class="hljs-selector-tag">VM</span> <span class="hljs-selector-tag">Zulu17</span><span class="hljs-selector-class">.68</span>+17<span class="hljs-selector-tag">-CA</span> (<span class="hljs-selector-tag">build</span> 17<span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.20</span>+8<span class="hljs-selector-tag">-LTS</span>, <span class="hljs-selector-tag">mixed</span> <span class="hljs-selector-tag">mode</span>, <span class="hljs-selector-tag">sharing</span>)</code></span></pre>


<p class="wp-block-paragraph">提示以上则表示成功</p>



<h4 class="wp-block-heading">四、构建应用</h4>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 构建所有平台（当前只有android）</span>
cordova build

<span class="hljs-comment"># 只构建安卓</span>
cordova build android
</code></span></pre>


<p class="wp-block-paragraph">编译成功后，apk 输出路径：</p>



<p class="wp-block-paragraph"><code>hello\platforms\android\app\build\outputs\apk\debug\app-debug.apk</code></p>



<p class="wp-block-paragraph">实录</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">D:\cordovademo\hello&gt;cordova build android
Checking Java JDK and Android SDK versions
ANDROID_HOME=C:\Users\maiwu\AppData\Local\Android\Sdk (recommended setting)
ANDROID_SDK_ROOT=<span class="hljs-literal">undefined</span> (DEPRECATED)
Using Android SDK: C:\Users\maiwu\AppData\Local\Android\Sdk

BUILD SUCCESSFUL <span class="hljs-keyword">in</span> <span class="hljs-number">9</span>s
<span class="hljs-number">1</span> actionable task: <span class="hljs-number">1</span> up-to-date
Subproject Path: CordovaLib
Subproject Path: app

&gt; Task :CordovaLib:compileDebugJavaWithJavac
<span class="hljs-attr">Note</span>: Some input files use or override a deprecated API.
Note: Recompile <span class="hljs-keyword">with</span> -Xlint:deprecation <span class="hljs-keyword">for</span> details.

&#91;Incubating] Problems report is available at: file:<span class="hljs-comment">///D:/cordovademo/hello/platforms/android/build/reports/problems/problems-report.html</span>

BUILD SUCCESSFUL <span class="hljs-keyword">in</span> <span class="hljs-number">11</span>s
<span class="hljs-number">50</span> actionable tasks: <span class="hljs-number">50</span> executed
Built the following apk(s):
        D:\cordovademo\hello\platforms\android\app\build\outputs\apk\debug\app-debug.apk</code></span></pre>


<p class="wp-block-paragraph">在目录下面看到apk文件D:\cordovademo\hello\platforms\android\app\build\outputs\apk\debug\</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="370" height="57" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-332.png" alt="" class="wp-image-6705" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-332.png 370w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-332-300x46.png 300w" sizes="auto, (max-width: 370px) 100vw, 370px" /></figure>



<h4 class="wp-block-heading">五、测试运行</h4>



<p class="wp-block-paragraph">方式 1：安卓模拟器运行</p>


<pre class="wp-block-code"><span><code class="hljs">cordova emulate android
</code></span></pre>


<p class="wp-block-paragraph">方式 2：USB 连接真机测试（开启 USB 调试）</p>


<pre class="wp-block-code"><span><code class="hljs">cordova run android
</code></span></pre>


<p class="wp-block-paragraph">我们使用真机来测试，也建议大家使用真机测试，用数据线链接android手机，然后手机开启调试模式。</p>



<p class="wp-block-paragraph">在hello目录下面 打开cmd，执行命令cordova run android</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="958" height="310" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-331.png" alt="" class="wp-image-6703" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-331.png 958w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-331-300x97.png 300w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-331-768x249.png 768w" sizes="auto, (max-width: 958px) 100vw, 958px" /></figure>



<p class="wp-block-paragraph">之后手机就可以安装一个app</p>



<p class="wp-block-paragraph">打开cordova的页面了</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="461" height="1024" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25-461x1024.jpg" alt="" class="wp-image-6704" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25-461x1024.jpg 461w, https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25-135x300.jpg 135w, https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25-768x1707.jpg 768w, https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25-691x1536.jpg 691w, https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25-922x2048.jpg 922w, https://www.foxdevelop.com/wp-content/uploads/2026/07/6541d0e7154e320c103c38f956918f25.jpg 1080w" sizes="auto, (max-width: 461px) 100vw, 461px" /></figure>



<h4 class="wp-block-heading">六、插件安装</h4>



<p class="wp-block-paragraph">访问手机原生能力</p>



<p class="wp-block-paragraph">Cordova 网页本身不能调用摄像头、蓝牙、文件、通知等硬件，<strong>必须安装插件</strong>。</p>



<p class="wp-block-paragraph">示例：摄像头插件</p>


<pre class="wp-block-code"><span><code class="hljs">cordova plugin add cordova-plugin-camera
</code></span></pre>


<p class="wp-block-paragraph">查看已安装插件</p>


<pre class="wp-block-code"><span><code class="hljs">cordova plugin ls
</code></span></pre>


<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">JS 调用原生 API 必须等待 <code>deviceready</code> 事件触发，页面加载完成不能直接调用插件！</p>
</blockquote>



<p class="wp-block-paragraph">基础模板示例（www/js/index.js）</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"deviceready"</span>, onDeviceReady, <span class="hljs-literal">false</span>);
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onDeviceReady</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// 设备就绪，可以调用所有cordova插件</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Cordova初始化完成"</span>);
}
</code></span></pre>


<p class="wp-block-paragraph">实录</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">cordovademo</span>\<span class="hljs-selector-tag">hello</span>&gt;<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">plugin</span> <span class="hljs-selector-tag">add</span> <span class="hljs-selector-tag">cordova-plugin-camera</span>
<span class="hljs-selector-tag">Installing</span> "<span class="hljs-selector-tag">cordova-plugin-camera</span>" <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">android</span>
<span class="hljs-selector-tag">Subproject</span> <span class="hljs-selector-tag">Path</span>: <span class="hljs-selector-tag">CordovaLib</span>
<span class="hljs-selector-tag">Subproject</span> <span class="hljs-selector-tag">Path</span>: <span class="hljs-selector-tag">app</span>
<span class="hljs-selector-tag">Adding</span> <span class="hljs-selector-tag">cordova-plugin-camera</span> <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">package</span><span class="hljs-selector-class">.json</span>

<span class="hljs-selector-tag">D</span>:\<span class="hljs-selector-tag">cordovademo</span>\<span class="hljs-selector-tag">hello</span>&gt;<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">plugin</span> <span class="hljs-selector-tag">ls</span>
<span class="hljs-selector-tag">cordova-plugin-camera</span> 8<span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.0</span> "<span class="hljs-selector-tag">Camera</span>"</code></span></pre>


<p class="wp-block-paragraph">下面修改index.html 为如下代码，测试摄像头功能</p>



<p class="wp-block-paragraph">首先说明一下，创建好项目后，我们的目录文件</p>



<p class="wp-block-paragraph">D:\cordovademo\hello</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="397" height="183" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-333.png" alt="" class="wp-image-6706" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-333.png 397w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-333-300x138.png 300w" sizes="auto, (max-width: 397px) 100vw, 397px" /></figure>



<p class="wp-block-paragraph">日后编写代码就在www中</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="390" height="99" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-334.png" alt="" class="wp-image-6707" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-334.png 390w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-334-300x76.png 300w" sizes="auto, (max-width: 390px) 100vw, 390px" /></figure>



<p class="wp-block-paragraph">修改index.html 为如下</p>


<pre class="wp-block-code"><span><code class="hljs language-cs">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset=<span class="hljs-string">"utf-8"</span>&gt;
    &lt;meta name=<span class="hljs-string">"viewport"</span> content=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;
    &lt;title&gt;Camera Test&lt;/title&gt;
    &lt;script src=<span class="hljs-string">"cordova.js"</span>&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;button id=<span class="hljs-string">"btnTake"</span>&gt;拍照&lt;/button&gt;
    &lt;br&gt;
    &lt;img id=<span class="hljs-string">"imgPreview"</span> style=<span class="hljs-string">"width:220px;"</span>&gt;

    &lt;script&gt;
        <span class="hljs-keyword">let</span> cameraReady = <span class="hljs-literal">false</span>;

        document.addEventListener(<span class="hljs-string">"deviceready"</span>, onDeviceReady, <span class="hljs-literal">false</span>);

        <span class="hljs-function">function <span class="hljs-title">onDeviceReady</span>(<span class="hljs-params"></span>)</span> {
            console.log(<span class="hljs-string">"deviceready 触发，Cordova就绪"</span>);
            cameraReady = <span class="hljs-literal">true</span>;
        }

        <span class="hljs-comment">// 改用addEventListener绑定（onclick容易出现时序冲突，推荐）</span>
        document.getElementById(<span class="hljs-string">"btnTake"</span>).addEventListener(<span class="hljs-string">"click"</span>, function(){
            <span class="hljs-keyword">if</span>(!cameraReady){
                alert(<span class="hljs-string">"等待Cordova初始化完成！"</span>);
                <span class="hljs-keyword">return</span>;
            }

            navigator.camera.getPicture(
                function(imageUri) {
                    console.log(<span class="hljs-string">"拍照成功："</span>, imageUri);
                    document.getElementById(<span class="hljs-string">"imgPreview"</span>).src = imageUri;
                },
                function(error) {
                    alert(<span class="hljs-string">"拍照失败："</span> + error);
                },
                {
                    destinationType: <span class="hljs-number">0</span>, <span class="hljs-comment">// Camera.DestinationType.FILE_URI = 0，直接用数字规避Camera未定义</span>
                    sourceType: <span class="hljs-number">1</span>,      <span class="hljs-comment">// Camera.PictureSourceType.CAMERA = 1</span>
                    quality: <span class="hljs-number">80</span>
                }
            );
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></span></pre>


<p class="wp-block-paragraph">再次运行cordova run android 命令</p>



<p class="wp-block-paragraph">会打开app，上面看到一个按钮，点击按钮，就会打开摄像头，然后就可以拍照了。</p>



<p class="wp-block-paragraph"></p>



<h4 class="wp-block-heading">* 七、merges 目录：多平台差异化文件</h4>



<p class="wp-block-paragraph">默认所有平台共用 <code>www</code> 资源。</p>



<p class="wp-block-paragraph">如果你想要安卓、iOS 界面资源不一样，使用 <code>merges</code> 文件夹：</p>


<pre class="wp-block-code"><span><code class="hljs language-php">merges/
├─android/
│  └─css/overrides.css  <span class="hljs-comment"># 仅安卓生效</span>
└─ios/
</code></span></pre>


<p class="wp-block-paragraph">构建时，merges 内文件会<strong>覆盖 www 同名文件</strong>。</p>



<h4 class="wp-block-heading">* 八、升级 Cordova</h4>



<p class="wp-block-paragraph"></p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment"># 全局升级cordova命令行</span>
npm update -g cordova

<span class="hljs-comment"># 安装指定版本</span>
npm install -g cordova@<span class="hljs-number">12.0</span><span class="hljs-number">.0</span>

<span class="hljs-comment"># 更新项目内安卓平台版本</span>
cordova platform update android --save
</code></span></pre>


<p class="wp-block-paragraph">命令汇总</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-id">#1</span> 创建项目
<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">create</span> <span class="hljs-selector-tag">testapp</span> <span class="hljs-selector-tag">com</span><span class="hljs-selector-class">.mytest</span><span class="hljs-selector-class">.app</span> <span class="hljs-selector-tag">TestApp</span>
<span class="hljs-selector-tag">cd</span> <span class="hljs-selector-tag">testapp</span>

<span class="hljs-selector-id">#2</span> 添加安卓平台
<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">platform</span> <span class="hljs-selector-tag">add</span> <span class="hljs-selector-tag">android</span>

<span class="hljs-selector-id">#3</span> 检查环境
<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">requirements</span>

<span class="hljs-selector-id">#4</span> 编译调试包
<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">build</span> <span class="hljs-selector-tag">android</span>

<span class="hljs-selector-id">#5</span> 真机运行
<span class="hljs-selector-tag">cordova</span> <span class="hljs-selector-tag">run</span> <span class="hljs-selector-tag">android</span>
</code></span></pre>]]></content:encoded>
					
					<wfw:commentRss>https://www.foxdevelop.com/zh/2026/07/28/6619/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>介绍和安装</title>
		<link>https://www.foxdevelop.com/zh/2026/07/28/%e4%bb%8b%e7%bb%8d%e5%92%8c%e5%ae%89%e8%a3%85/</link>
					<comments>https://www.foxdevelop.com/zh/2026/07/28/%e4%bb%8b%e7%bb%8d%e5%92%8c%e5%ae%89%e8%a3%85/#respond</comments>
		
		<dc:creator><![CDATA[jack]]></dc:creator>
		<pubDate>Tue, 28 Jul 2026 08:31:30 +0000</pubDate>
				<category><![CDATA[Cordova 教程]]></category>
		<guid isPermaLink="false">https://www.foxdevelop.com/?p=6611</guid>

					<description><![CDATA[Apache Cordova 是一套开源移动开发框架。开发者 ...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Apache Cordova 是一套开源移动开发框架。开发者可以使用标准 Web 技术 ——HTML5、CSS3 与 JavaScript 进行跨平台开发。应用运行在面向各平台封装的容器内，通过兼容标准的 API 绑定调用设备能力，例如传感器、本地数据、网络状态等。</p>



<p class="wp-block-paragraph">一套代码库，适配多平台，并且是免费开源。</p>



<p class="wp-block-paragraph">如果你符合以下场景，可以选择 Apache Cordova：</p>



<ul class="wp-block-list">
<li>你是移动开发者，想要一套应用部署到多个平台，无需针对各平台语言与工具链重复开发； </li>



<li>你是 Web 开发者，希望将网页应用打包，上架到各大应用商店分发； </li>



<li>你是移动开发者，需要混合原生组件与 WebView（专用浏览器窗口），让网页代码访问设备底层 API；或是需要开发插件，打通原生代码与 WebView 之间的调用通道。</li>
</ul>



<p class="wp-block-paragraph">Cordova 应用由多个组件构成。下图为 Cordova 应用架构高层概览。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="744" height="650" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-314.png" alt="" class="wp-image-6613" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-314.png 744w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-314-300x262.png 300w" sizes="auto, (max-width: 744px) 100vw, 744px" /></figure>



<h4 class="wp-block-heading">WebView（网页视图）</h4>



<p class="wp-block-paragraph">搭载 Cordova 能力的 WebView 可以承载应用完整界面。在部分平台中，它也能作为组件嵌入混合应用，和原生控件共同工作。</p>



<h4 class="wp-block-heading">Web App（网页应用）</h4>



<p class="wp-block-paragraph">这里存放你的业务代码。应用本身以网页形式实现，默认入口为本地 <code>index.html</code> 文件，页面引用所需 CSS、JavaScript、图片、媒体资源等文件。整个应用运行在原生容器内部的 WebView 中，最终打包分发至各个应用商店。</p>



<p class="wp-block-paragraph">容器中有一个至关重要的文件：<strong>config.xml</strong>。该文件记录应用基础信息，并定义各项运行参数，例如是否支持屏幕横竖屏切换。</p>



<h4 class="wp-block-heading">Plugins（插件）</h4>



<p class="wp-block-paragraph">插件是 Cordova 生态的核心组成部分。它提供桥梁，实现 Cordova 与原生代码互相通信，并封装各类设备标准 API，让 JavaScript 能够调用原生代码。</p>



<p class="wp-block-paragraph">Apache Cordova 官方维护一组<strong>核心插件</strong>，提供访问设备基础能力的接口：电池状态、相机、通讯录等。</p>



<p class="wp-block-paragraph">除官方核心插件外，还有大量第三方插件，封装各类并非全平台通用的扩展功能。你可以通过插件检索平台或 npm 查找 Cordova 插件；也可以按照《插件开发指南》自主开发插件。典型场景：实现 Cordova 和自定义原生组件之间的通信。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">注意：新建 Cordova 项目默认<strong>不自带任何插件</strong>，这是新版默认规则。无论核心插件还是第三方插件，都需要手动显式安装。</p>
</blockquote>



<p class="wp-block-paragraph">Cordova *<em>不提供 UI 控件与 MV</em> 前端框架 **，仅提供承载网页代码的运行环境。如果你需要界面组件或前端架构框架，需要自行选择并集成到项目中。</p>



<h4 class="wp-block-heading">两种开发流程</h4>



<p class="wp-block-paragraph">Cordova 提供两套主流工作流来构建移动应用。很多场景下两套流程都能实现目标，但各有优势：</p>



<h5 class="wp-block-heading">跨平台工作流（CLI 命令行工具）</h5>



<p class="wp-block-paragraph">适合目标平台较多、极少需要平台专属定制开发的场景。核心工具为 <code>cordova CLI</code>。 CLI 是高层工具，支持一次性为多个平台构建项目，屏蔽底层脚本细节： CLI 将通用网页资源复制到各个平台子目录，自动完成平台配置，执行构建脚本生成安装包；同时提供统一命令行接口管理插件。</p>



<h5 class="wp-block-heading">平台专属工作流</h5>



<p class="wp-block-paragraph">适合聚焦单一平台开发、需要深度底层修改项目的场景</p>



<p class="wp-block-paragraph">典型使用场景：在应用内混合自定义原生组件与 Cordova WebView</p>



<p class="wp-block-paragraph">如果你需要直接修改平台 SDK 内项目文件，就选用这套流程。</p>



<p class="wp-block-paragraph">该工作流基于各平台专属底层脚本，搭配独立工具 Plugman 管理插件。 虽然也能用它做多平台开发，但成本更高：没有高层工具统一管理，每个平台需要单独构建、单独维护插件配置。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">一旦从 CLI 模式切换为平台 SDK + 底层脚本模式，<strong>无法回退</strong>。</p>



<p class="wp-block-paragraph">CLI 会维护一套跨平台公共源码，每次构建都会覆盖平台目录内的代码。如果你需要保留对平台目录文件的修改，就必须切换到底层脚本模式；该模式不再读取公共网页源码，直接使用平台目录内独立代码。</p>
</blockquote>



<h4 class="wp-block-heading">安装 Cordova</h4>



<p class="wp-block-paragraph">安装方式取决于你选择的开发流程：</p>



<p class="wp-block-paragraph">我们以跨平台 CLI 工作流模式为例。</p>



<p class="wp-block-paragraph">Cordova 命令行工具（CLI）以 npm 包形式分发。</p>



<p class="wp-block-paragraph">按照以下步骤安装 cordova CLI：</p>



<ol class="wp-block-list">
<li>下载并安装 <a href="https://nodejs.org/en/download">Node.js</a>。安装完成后，你应当可以在终端直接调用 <code>node</code> 和 <code>npm</code> 命令。</li>



<li>（可选）如果你尚未安装 Git，下载并安装 Git 客户端。安装后，可在命令提示符（终端）执行 <code>git</code> 命令。当需要通过 Git 仓库地址下载资源时，Cordova CLI 和 npm 会调用 git。</li>



<li>使用 Node.js 自带的 npm 工具安装 cordova 模块，npm 将自动完成下载。</li>
</ol>



<p class="wp-block-paragraph">安装nodejs</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="144" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-315-1024x144.png" alt="" class="wp-image-6614" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-315-1024x144.png 1024w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-315-300x42.png 300w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-315-768x108.png 768w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-315.png 1141w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">安装nodejs比较简单，下载msi（对于windows系统），直接双击安装就可以，安装过程就基本下一步就可以。安装完毕后</p>



<p class="wp-block-paragraph">打开CMD终端，检验是否安装成功</p>



<p class="wp-block-paragraph">输入两条命令，<strong>两条都正常输出版本号才算成功</strong>（输出版本号，如下图）</p>


<pre class="wp-block-code"><span><code class="hljs">node -v
npm -v</code></span></pre>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="268" height="96" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-316.png" alt="" class="wp-image-6615"/></figure>



<p class="has-medium-font-size wp-block-paragraph"><mark style="background-color:#03c4eb" class="has-inline-color">成功安装nodejs后，下面开始安装cordova</mark></p>



<p class="wp-block-paragraph"><strong>macOS / Linux 系统：</strong></p>


<pre class="wp-block-code"><span><code class="hljs">npm install -g cordova</code></span></pre>


<p class="wp-block-paragraph">macOS 与 Linux 用户：若需要将工具安装到 <code>/usr/local/share</code> 这类权限受限目录，执行 npm 命令时可能需要加上 <code>sudo</code>。<br>但如果你使用了 nvm / nave 这类版本管理工具，或者当前目录拥有写入权限，则可以不加 <code>sudo</code>。</p>



<p class="wp-block-paragraph">同时建议：尽量避免搭配 <code>sudo</code> 使用 npm，防止出现权限、包安装异常等问题。<br>推荐使用 nvm（Node Version Manager）或 nave 版本管理器管理 Node.js 和 npm，安装包时通常无需 sudo。</p>



<p class="wp-block-paragraph"><strong>Windows 系统：</strong></p>


<pre class="wp-block-code"><span><code class="hljs">C:\&gt;npm install -g cordova</code></span></pre>


<p class="wp-block-paragraph">参数 <code>-g</code> 代表全局安装 cordova；如果不加该参数，只会安装在当前目录的 <code>node_modules</code> 子文件夹内。</p>



<p class="wp-block-paragraph">安装完成后，在终端直接执行 <code>cordova</code>（不带任何参数），若正常输出帮助信息即代表安装成功。</p>



<p class="wp-block-paragraph">安装过程 显示“added 217 packages in 14s” </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="959" height="227" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-317.png" alt="" class="wp-image-6616" srcset="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-317.png 959w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-317-300x71.png 300w, https://www.foxdevelop.com/wp-content/uploads/2026/07/image-317-768x182.png 768w" sizes="auto, (max-width: 959px) 100vw, 959px" /></figure>



<p class="wp-block-paragraph">检验是否安装成功</p>


<pre class="wp-block-code"><span><code class="hljs">cordova -v</code></span></pre>


<p class="wp-block-paragraph">正常输出版本号（例如 <code>13.0.0</code>）就代表就绪。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="246" height="46" src="https://www.foxdevelop.com/wp-content/uploads/2026/07/image-318.png" alt="" class="wp-image-6617"/></figure>



<h4 class="wp-block-heading">系统要求与版本支持</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Cordova CLI 版本</th><th>支持的 Node.js 最低版本</th></tr></thead><tbody><tr><td>13.x</td><td>&gt;=20.17.0</td></tr><tr><td>12.x</td><td>&gt;=16.13.0</td></tr><tr><td>11.x</td><td>&gt;=12.0.0</td></tr><tr><td>10.x</td><td>&gt;=10.0.0</td></tr><tr><td>9.x</td><td>>=6.0.0<br></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">现在安装成功，接下来，我们就开始正式学习cordova了。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.foxdevelop.com/zh/2026/07/28/%e4%bb%8b%e7%bb%8d%e5%92%8c%e5%ae%89%e8%a3%85/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
